Use the Flink Log Connector to consume log data from Simple Log Service. The connector supports both open source Flink and Realtime Compute for Apache Flink.
Prerequisites
Simple Log Service is activated.
Simple Log Service SDK for Python is initialized.
-
A Simple Log Service project and Logstore are created. For more information, see Manage projects and Create a basic Logstore.
Overview
The Flink Log Connector has two components:
-
The consumer reads data from Simple Log Service with exactly-once semantics and shard load balancing.
-
The producer writes data to Simple Log Service.
Add the following Maven dependencies to your project:
<dependency> <groupId>com.aliyun.openservices</groupId> <artifactId>flink-log-connector</artifactId> <version>0.1.46</version> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>2.5.0</version> </dependency>
More code examples are available in the aliyun-log-flink-connector repository on GitHub.
Flink Log Connector version 0.1.46 introduces AliyunLogSource and AliyunLogSink, which are new interfaces based on the FLIP-27 specification, along with a SQL Connector. Use the new interfaces for new jobs. The legacy interfaces FlinkLogConsumer and FlinkLogProducer are planned for removal.
AliyunLogSource (DataStream Source)
AliyunLogSource is based on the FLIP-27 specification and integrates with Flink through env.fromSource(...). The split and cursor states of the Source participate in Flink checkpoints for job failover recovery. If a ConsumerGroup is configured, checkpoints can also be committed to the Simple Log Service server for consumption progress monitoring.
Basic usage:
Properties properties = new Properties(); properties.setProperty(ConfigConstants.LOG_CHECKPOINT_MODE, CheckpointMode.ON_CHECKPOINTS.name()); properties.setProperty(ConfigConstants.LOG_MAX_NUMBER_PER_FETCH, "100"); AliyunLogSource<MyRecord> source = AliyunLogSource.<MyRecord>builder() .setProject("your-project") .setLogStore("your-logstore") .setEndpoint("cn-hangzhou.log.aliyuncs.com") .setCredentials(accessKeyId, accessKeySecret) .setConsumerGroup("flink-source-consumer") .setStartingPosition(StartingPosition.EARLIEST) .setProperties(properties) .setDeserializer(new MyDeserializer()) .build(); DataStream<MyRecord> stream = env.fromSource( source, WatermarkStrategy.noWatermarks(), "aliyun-log-source");
Source parameters
Parameter / Builder method |
Required |
Default |
Description |
| Yes | N/A | The Simple Log Service project to consume. |
| Yes | N/A | The Logstore to consume. |
| Yes | N/A | The Simple Log Service endpoint, for example, |
| Yes | N/A | The AccessKey ID and AccessKey secret used to access Simple Log Service. |
| Yes | N/A | The deserializer that converts Simple Log Service pull results into Flink records. |
| No | N/A | The Simple Log Service consumer group name. Used to read or commit server-side checkpoints. |
| No |
| The starting position for consumption. Supported values: |
| No |
| The fallback position used when the starting position is |
| No |
| The maximum number of LogGroups pulled from a single shard per request. |
| No |
| The wait interval in milliseconds before the next pull when no data is returned. |
| No |
| The polling interval in milliseconds for detecting shard splits or merges. |
| No |
| The server-side checkpoint commit mode. |
| No | N/A | A Unix timestamp in seconds. Consumption stops for the corresponding shard after this time point. This parameter is useful for backfilling data offline. |
| No |
| The maximum number of retries for general errors. |
| No |
| The request signature version. Valid values: |
Custom deserializer
Implement the AliyunLogDeserializationSchema<T> interface and complete the log expansion and field conversion in the deserialize method. A PullLogsResult may contain multiple LogGroups, and each LogGroup may contain multiple logs. The deserializer can output zero, one, or more Flink records to the Collector.
The following example expands each Simple Log Service log into a POJO that contains metadata and a content map:
public class ContentMapDeserializer implements AliyunLogDeserializationSchema<SlsLogRecord> { @Override public TypeInformation<SlsLogRecord> getProducedType() { return TypeInformation.of(SlsLogRecord.class); } @Override public void deserialize(PullLogsResult record, Collector<SlsLogRecord> out) { for (LogGroupData logGroupData : record.getLogGroupList()) { FastLogGroup logGroup = logGroupData.GetFastLogGroup(); for (int logIndex = 0; logIndex < logGroup.getLogsCount(); logIndex++) { FastLog log = logGroup.getLogs(logIndex); Map<String, String> fields = new LinkedHashMap<>(); for (int contentIndex = 0; contentIndex < log.getContentsCount(); contentIndex++) { FastLogContent content = log.getContents(contentIndex); fields.put(content.getKey(), content.getValue()); } out.collect(new SlsLogRecord( log.getTime(), logGroup.getTopic(), logGroup.getSource(), record.getShard(), record.getCursor(), fields)); } } } }
Complete consumption example
This example obtains access credentials from environment variables and resumes consumption from a server-side ConsumerGroup checkpoint. If no server-side checkpoint exists, consumption starts from the earliest position.
package com.aliyun.openservices.log.flink.sample; import com.aliyun.openservices.log.flink.ConfigConstants; import com.aliyun.openservices.log.flink.model.CheckpointMode; import com.aliyun.openservices.log.flink.source.AliyunLogSource; import com.aliyun.openservices.log.flink.source.StartingPosition; import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.configuration.CheckpointingOptions; import org.apache.flink.configuration.Configuration; import org.apache.flink.streaming.api.CheckpointingMode; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.CheckpointConfig; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import java.util.Properties; public class AliyunLogConsumerSample { private static final String SLS_ENDPOINT = "cn-hangzhou.log.aliyuncs.com"; private static final String SLS_PROJECT = "your-project"; private static final String SLS_LOGSTORE = "your-logstore"; private static final String CONSUMER_GROUP = "your-consumer-group"; public static void main(String[] args) throws Exception { String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"); String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"); Configuration configuration = new Configuration(); configuration.setString(CheckpointingOptions.CHECKPOINTS_DIRECTORY, "file:///tmp/flink-checkpoints"); StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(configuration); env.setParallelism(2); env.enableCheckpointing(60000); env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE); env.getCheckpointConfig().enableExternalizedCheckpoints( CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION); Properties sourceProperties = new Properties(); sourceProperties.setProperty(ConfigConstants.LOG_MAX_NUMBER_PER_FETCH, "100"); sourceProperties.setProperty(ConfigConstants.LOG_FETCH_DATA_INTERVAL_MILLIS, "100"); sourceProperties.setProperty(ConfigConstants.LOG_SHARDS_DISCOVERY_INTERVAL_MILLIS, "30000"); sourceProperties.setProperty(ConfigConstants.LOG_CHECKPOINT_MODE, CheckpointMode.ON_CHECKPOINTS.name()); AliyunLogSource<SlsLogRecord> source = AliyunLogSource.<SlsLogRecord>builder() .setEndpoint(SLS_ENDPOINT) .setProject(SLS_PROJECT) .setLogStore(SLS_LOGSTORE) .setCredentials(accessKeyId, accessKeySecret) .setConsumerGroup(CONSUMER_GROUP) .setStartingPosition(StartingPosition.CHECKPOINT) .setFallbackPosition(StartingPosition.EARLIEST) .setProperties(sourceProperties) .setDeserializer(new ContentMapDeserializer()) .build(); DataStream<SlsLogRecord> stream = env.fromSource( source, WatermarkStrategy.noWatermarks(), "aliyun-log-source"); stream.print(); env.execute("aliyun log consumer"); } }
AliyunLogSink (DataStream Sink)
AliyunLogSink integrates with Flink through stream.sinkTo(...). The Sink uses the Simple Log Service Producer SDK to send data asynchronously and waits for submitted requests to complete during Flink checkpoints or job termination, providing at-least-once semantics.
The custom serializer must implement the AliyunLogSerializationSchema<T> interface. A single input element can output zero, one, or more Simple Log Service records through Collector<SinkRecord>.
Basic usage:
class MySerializationSchema implements AliyunLogSerializationSchema<String> { @Override public void serialize(String element, Collector<SinkRecord> output) { LogItem item = new LogItem((int) (System.currentTimeMillis() / 1000L)); item.PushBack("message", element); SinkRecord record = new SinkRecord(); record.setTopic("flink"); record.setSource("flink-job"); record.setLogItem(item); output.collect(record); } } AliyunLogSink<String> sink = AliyunLogSink.<String>builder() .setProject("your-project") .setLogStore("your-logstore") .setEndpoint("cn-hangzhou.log.aliyuncs.com") .setCredentials(accessKeyId, accessKeySecret) .setSerializer(new MySerializationSchema()) .setProperty(ConfigConstants.FLUSH_INTERVAL_MS, "100") .build(); stream.sinkTo(sink).name("aliyun-log-sink");
Sink parameters
Parameter / Builder method |
Required |
Default |
Description |
| Yes | N/A | The target project for writing. |
| Yes | N/A | The default target Logstore for writing. This value is overridden when a |
| Yes | N/A | The Simple Log Service endpoint. |
| Yes | N/A | The AccessKey ID and AccessKey secret used to access Simple Log Service. |
| Yes | N/A | The serializer that converts Flink records into |
| No | Producer SDK default | The maximum time in milliseconds that logs are cached on the client before being sent. |
| No | Producer SDK default | The maximum number of retries for failed send operations. |
| No | Producer SDK default | The number of I/O threads used to send logs. |
| No | Producer SDK default | The total cache size available to the Producer client. |
| No | Producer SDK default | The maximum time in milliseconds that a send call blocks when the cache is full or resources are insufficient. |
| No |
| The request signature version. Valid values: |
To control which shard data is written to, call record.setHashKey(...) on the SinkRecord to set a hash key. To dynamically write to different Logstores, call record.setLogstore(...) to override the Sink default Logstore.
Complete write example
This example uses env.fromSequence(...) to generate test data and writes it to Simple Log Service through AliyunLogSerializationSchema.
package com.aliyun.openservices.log.flink.sample; import com.aliyun.openservices.log.common.LogItem; import com.aliyun.openservices.log.flink.ConfigConstants; import com.aliyun.openservices.log.flink.data.SinkRecord; import com.aliyun.openservices.log.flink.model.AliyunLogSerializationSchema; import com.aliyun.openservices.log.flink.sink.AliyunLogSink; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.util.Collector; public class AliyunLogProducerSample { private static final String SLS_ENDPOINT = "cn-hangzhou.log.aliyuncs.com"; private static final String SLS_PROJECT = "your-project"; private static final String SLS_LOGSTORE = "your-logstore"; public static void main(String[] args) throws Exception { String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"); String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"); StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(3); DataStream<Long> events = env.fromSequence(1, 1000); AliyunLogSink<Long> sink = AliyunLogSink.<Long>builder() .setEndpoint(SLS_ENDPOINT) .setProject(SLS_PROJECT) .setLogStore(SLS_LOGSTORE) .setCredentials(accessKeyId, accessKeySecret) .setSerializer(new LongSerializer()) .setProperty(ConfigConstants.FLUSH_INTERVAL_MS, "100") .setProperty(ConfigConstants.MAX_RETRIES, "10") .build(); events.sinkTo(sink).name("aliyun-log-sink"); env.execute("aliyun log producer"); } public static class LongSerializer implements AliyunLogSerializationSchema<Long> { @Override public void serialize(Long element, Collector<SinkRecord> output) { LogItem logItem = new LogItem((int) (System.currentTimeMillis() / 1000L)); logItem.PushBack("id", String.valueOf(element)); logItem.PushBack("message", "message-" + element); SinkRecord record = new SinkRecord(); record.setTopic("flink"); record.setSource("flink-job"); record.setLogItem(logItem); output.collect(record); } } }
SQL Connector
The SQL Connector identifier is aliyun-log. The same connector supports both SQL Source and SQL Sink. In a Source table, regular columns are read from the Simple Log Service log content by matching column names. In a Sink table, regular columns are written to log content by column names.
SQL Source example
CREATE TABLE sls_logs ( `__time__` TIMESTAMP(3), `__topic__` STRING, `__source__` STRING, level STRING, message STRING, status_code INT ) WITH ( 'connector' = 'aliyun-log', 'endpoint' = 'cn-hangzhou.log.aliyuncs.com', 'project' = 'your-project', 'logstore' = 'your-logstore', 'access.key.id' = '${ACCESS_KEY_ID}', 'access.key.secret' = '${ACCESS_KEY_SECRET}', 'consumer-group' = 'flink-sql-consumer', 'scan.startup.mode' = 'checkpoint', 'scan.startup.default-position' = 'earliest', 'checkpoint.mode' = 'on-checkpoints', 'max.number.per.fetch' = '100', 'shards.discovery.interval.ms' = '60000', 'ignore-parse-errors' = 'true' );
SQL Sink example
CREATE TABLE sls_sink ( `__time__` TIMESTAMP(3), `__topic__` STRING, `__source__` STRING, level STRING, message STRING, status_code INT ) WITH ( 'connector' = 'aliyun-log', 'endpoint' = 'cn-hangzhou.log.aliyuncs.com', 'project' = 'your-project', 'logstore' = 'your-logstore', 'access.key.id' = '${ACCESS_KEY_ID}', 'access.key.secret' = '${ACCESS_KEY_SECRET}', 'sink.topic' = 'flink-sql', 'sink.source' = 'flink-job', 'flush.interval.ms' = '100', 'max.retries' = '5' );
SQL WITH parameters
SQL parameter |
Applicable direction |
Required |
Default |
Description |
| Source / Sink | Yes | N/A | Fixed value: |
| Source / Sink | Yes | N/A | The Simple Log Service endpoint. |
| Source / Sink | Yes | N/A | The Simple Log Service project. |
| Source / Sink | Yes | N/A | The Logstore to read from (Source) or the default Logstore to write to (Sink). |
| Source / Sink | Yes | N/A | The AccessKey ID used to access Simple Log Service. |
| Source / Sink | Yes | N/A | The AccessKey secret used to access Simple Log Service. |
| Source | No | N/A | The consumer group name. Used to read or commit server-side checkpoints. |
| Source | No |
| The starting position for consumption. Valid values: |
| Source | No |
| The server-side checkpoint commit mode. Valid values: |
| Source | No |
| The maximum number of LogGroups pulled from a single shard per request. |
| Source | No |
| Specifies whether to output |
| Sink | No |
| The default LogGroup topic used for writes. This value can be overridden by the |
| Sink | No | N/A | The default LogGroup source used for writes. This value can be overridden by the |
| Sink | No | Producer SDK default | The maximum time that logs are cached on the client before being sent. |
| Source / Sink | No |
| The request signature version. Valid values: |
SQL Source metadata columns
The following column names are built-in metadata columns for reads. When declared, values are read from the Simple Log Service log or shard metadata instead of from a log content field with the same name.
Metadata column |
Recommended type |
Description |
|
| The Simple Log Service log time. |
|
| The LogGroup topic. |
|
| The LogGroup source. |
|
| The shard ID of the current record. |
|
| The cursor of the current pull batch. |
SQL Sink metadata columns
The following column names are built-in metadata columns for writes. When declared, these columns are not written as regular content.
Metadata column |
Recommended type |
Description |
|
| The Simple Log Service log time. A timestamp type is written in seconds. |
|
| Overrides |
|
| Overrides |
|
| Overrides the |
|
| Sets the shard hash key for the current record. |
RAM permissions
When you use the Flink Log Connector to access Simple Log Service, you must grant the corresponding API permissions to the RAM user or role.
Permissions required for Source reads
API |
Resource |
|
|
|
|
|
|
|
|
Permissions required for Sink writes
API |
Resource |
|
|
Flink Log Consumer
Legacy API. Planned for removal. Use AliyunLogSource for new jobs.
The Flink Log Consumer subscribes to a Logstore and provides exactly-once semantics. It detects shard changes automatically, eliminating manual shard management.
Each subtask consumes data from a subset of shards. If shards are split or merged, the subtask's shard assignment updates automatically.
The consumer uses the following API operations:
-
GetCursorOrData
Retrieves data from a shard. Frequent calls may exceed the shard limit. Use
ConfigConstants.LOG_FETCH_DATA_INTERVAL_MILLISandConfigConstants.LOG_MAX_NUMBER_PER_FETCHto control the call interval and the number of logs retrieved per call. For more information, see Shards.Example:
configProps.put(ConfigConstants.LOG_FETCH_DATA_INTERVAL_MILLIS, "100"); configProps.put(ConfigConstants.LOG_MAX_NUMBER_PER_FETCH, "100"); -
ListShards
Retrieves all shards and their statuses. Adjust the call interval to detect shard changes promptly:
// Call the ListShards API operation every 30s. configProps.put(ConfigConstants.LOG_SHARDS_DISCOVERY_INTERVAL_MILLIS, "30000"); -
CreateConsumerGroup
Creates a consumer group to sync checkpoints when you enable consumption progress monitoring.
-
UpdateCheckPoint
Syncs Flink snapshots to the consumer group in Simple Log Service.
-
Configure startup parameters.
The following example uses
java.util.Propertiesfor configuration. All parameters are defined in theConfigConstantsclass.Properties configProps = new Properties(); // The endpoint of Simple Log Service. configProps.put(ConfigConstants.LOG_ENDPOINT, "cn-hangzhou.log.aliyuncs.com"); // In this example, the AccessKey ID and AccessKey secret are obtained from environment variables. String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"); String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"); configProps.put(ConfigConstants.LOG_ACCESSKEYID,accessKeyId); configProps.put(ConfigConstants.LOG_ACCESSKEY,accessKeySecret); // The Simple Log Service project. String project = "your-project"; // The Simple Log Service Logstore. String logstore = "your-logstore"; // The position from which to start consuming logs. configProps.put(ConfigConstants.LOG_CONSUMER_BEGIN_POSITION, Consts.LOG_END_CURSOR); // The method to deserialize messages from Simple Log Service. FastLogGroupDeserializer deserializer = new FastLogGroupDeserializer(); final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); DataStream<FastLogGroupList> dataStream = env.addSource( new FlinkLogConsumer<FastLogGroupList>(project, logstore, deserializer, configProps) ); dataStream.addSink(new SinkFunction<FastLogGroupList>() { @Override public void invoke(FastLogGroupList logGroupList, Context context) throws Exception { for (FastLogGroup logGroup : logGroupList.getLogGroups()) { int logsCount = logGroup.getLogsCount(); String topic = logGroup.getTopic(); String source = logGroup.getSource(); for (int i = 0; i < logsCount; ++i) { FastLog row = logGroup.getLogs(i); for (int j = 0; j < row.getContentsCount(); ++j) { FastLogContent column = row.getContents(j); // Process logs. System.out.println(column.getKey()); System.out.println(column.getValue()); } } } } }); // Or, use RawLogGroupListDeserializer. RawLogGroupListDeserializer rawLogGroupListDeserializer = new RawLogGroupListDeserializer(); final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); DataStream<RawLogGroupList> rawLogGroupListDataStream = env.addSource( new FlinkLogConsumer<RawLogGroupList>(project, logstore, rawLogGroupListDeserializer, configProps) ); rawLogGroupListDataStream.addSink(new SinkFunction<RawLogGroupList>() { @Override public void invoke(RawLogGroupList logGroupList, Context context) throws Exception { for (RawLogGroup logGroup : logGroupList.getRawLogGroups()) { String topic = logGroup.getTopic(); String source = logGroup.getSource(); for (RawLog row : logGroup.getLogs()) { // Process logs. } } } });NoteThe number of subtasks is independent of the shard count. If there are more shards than subtasks, each subtask consumes data from a unique set of shards. If there are fewer shards, some subtasks remain idle until new shards are created.
-
Set the start position for consumption.
Set
ConfigConstants.LOG_CONSUMER_BEGIN_POSITIONto one of the following values:-
Consts.LOG_BEGIN_CURSOR: Starts consumption from the beginning of the shard, which is the oldest data in the shard. -
Consts.LOG_END_CURSOR: Starts consumption from the end of the shard, which is the latest data in the shard. -
Consts.LOG_FROM_CHECKPOINT: Starts consumption from a checkpoint saved in a specific consumer group. UseConfigConstants.LOG_CONSUMERGROUPto specify the consumer group. -
UnixTimestamp: A string that represents a UNIX timestamp in seconds. Consumption starts from the data logged after this timestamp.
Example:
configProps.put(ConfigConstants.LOG_CONSUMER_BEGIN_POSITION, Consts.LOG_BEGIN_CURSOR); configProps.put(ConfigConstants.LOG_CONSUMER_BEGIN_POSITION, Consts.LOG_END_CURSOR); configProps.put(ConfigConstants.LOG_CONSUMER_BEGIN_POSITION, "1512439000"); configProps.put(ConfigConstants.LOG_CONSUMER_BEGIN_POSITION, Consts.LOG_FROM_CHECKPOINT);NoteIf Flink recovers from its own StateBackend, these settings are ignored and consumption resumes from the StateBackend checkpoint.
-
-
Optional: Set up consumption progress monitoring.
The Flink Log Consumer supports consumption progress monitoring to retrieve the real-time consumption position of each shard. For more information, see Step 2: View the status of a consumer group.
Example:
configProps.put(ConfigConstants.LOG_CONSUMERGROUP, "your consumer group name");NoteIf configured, the Flink Log Consumer creates a consumer group. If the consumer group already exists, no action is taken. Snapshots are automatically synced to the consumer group, and you can view consumption progress in the Simple Log Service console.
-
Configure disaster recovery and exactly-once semantics.
When Flink checkpointing is enabled, the consumer periodically saves consumption progress. If a task fails, Flink resumes from the latest checkpoint.
The checkpoint interval determines how much data is re-consumed on failure:
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // Enable Flink exactly-once semantics. env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE); // Save a checkpoint every 5s. env.enableCheckpointing(5000);For more information about Flink checkpoints, see Checkpoints in the Flink documentation.
Flink Log Producer
Legacy API. Planned for removal. Use AliyunLogSink for new jobs.
The Flink Log Producer writes data to Simple Log Service.
The Flink Log Producer supports only at-least-once semantics. Data may be duplicated on failure, but no data is lost.
The producer uses the following API operations:
-
PutLogs
-
ListShards
-
Initialize the Flink Log Producer.
Initialize the Properties configuration parameters.
The initialization is similar to the consumer. The following parameters are available (defaults are used if not specified):
// The number of I/O threads used to send data. The default value is the number of CPU cores. ConfigConstants.IO_THREAD_NUM // The maximum time that logs can be cached before being sent. The default value is 2,000 milliseconds. ConfigConstants.FLUSH_INTERVAL_MS // The total amount of memory that a task can use. The default value is 100 MB. ConfigConstants.TOTAL_SIZE_IN_BYTES // The maximum blocking time for sending logs when the memory limit is reached. The unit is milliseconds. The default value is 60s. ConfigConstants.MAX_BLOCK_TIME_MS // The maximum number of retries. The default value is 10. ConfigConstants.MAX_RETRIESOverride
LogSerializationSchemaand define a method to serialize data into aRawLogGroup.A
RawLogGroupis a collection of logs. For more information about the fields, see Log.To write data to a specific shard, use
LogPartitionerto generate a hash key. If you do not configure a partitioner, data is written to random shards.For example:
FlinkLogProducer<String> logProducer = new FlinkLogProducer<String>(new SimpleLogSerializer(), configProps); logProducer.setCustomPartitioner(new LogPartitioner<String>() { // Generate a 32-bit hash value. public String getHashKey(String element) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(element.getBytes()); String hash = new BigInteger(1, md.digest()).toString(16); while(hash.length() < 32) hash = "0" + hash; return hash; } catch (NoSuchAlgorithmException e) { } return "0000000000000000000000000000000000000000000000000000000000000000"; } }); -
Write simulated data to Simple Log Service:
// Serialize data into the Simple Log Service data format. class SimpleLogSerializer implements LogSerializationSchema<String> { public RawLogGroup serialize(String element) { RawLogGroup rlg = new RawLogGroup(); RawLog rl = new RawLog(); rl.setTime((int)(System.currentTimeMillis() / 1000)); rl.addContent("message", element); rlg.addLog(rl); return rlg; } } public class ProducerSample { public static String sEndpoint = "cn-hangzhou.log.aliyuncs.com"; // In this example, the AccessKey ID and AccessKey secret are obtained from environment variables. public static String sAccessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"); public static String sAccessKey = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"); public static String sProject = "ali-cn-hangzhou-sls-admin"; public static String sLogstore = "test-flink-producer"; private static final Logger LOG = LoggerFactory.getLogger(ConsumerSample.class); public static void main(String[] args) throws Exception { final ParameterTool params = ParameterTool.fromArgs(args); final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.getConfig().setGlobalJobParameters(params); env.setParallelism(3); DataStream<String> simpleStringStream = env.addSource(new EventsGenerator()); Properties configProps = new Properties(); // The endpoint of Simple Log Service. configProps.put(ConfigConstants.LOG_ENDPOINT, sEndpoint); // The user's AccessKey. configProps.put(ConfigConstants.LOG_ACCESSKEYID, sAccessKeyId); configProps.put(ConfigConstants.LOG_ACCESSKEY, sAccessKey); // The Simple Log Service project to which logs are written. configProps.put(ConfigConstants.LOG_PROJECT, sProject); // The Simple Log Service Logstore to which logs are written. configProps.put(ConfigConstants.LOG_LOGSTORE, sLogstore); FlinkLogProducer<String> logProducer = new FlinkLogProducer<String>(new SimpleLogSerializer(), configProps); simpleStringStream.addSink(logProducer); env.execute("flink log producer"); } // Simulate log generation. public static class EventsGenerator implements SourceFunction<String> { private boolean running = true; @Override public void run(SourceContext<String> ctx) throws Exception { long seq = 0; while (running) { Thread.sleep(10); ctx.collect((seq++) + "-" + RandomStringUtils.randomAlphabetic(12)); } } @Override public void cancel() { running = false; } } }
Consumption example
This example reads data as FastLogGroupList, converts entries to JSON strings with flatMap, and writes the output to a text file.
package com.aliyun.openservices.log.flink.sample; import com.alibaba.fastjson.JSONObject; import com.aliyun.openservices.log.common.FastLog; import com.aliyun.openservices.log.common.FastLogGroup; import com.aliyun.openservices.log.flink.ConfigConstants; import com.aliyun.openservices.log.flink.FlinkLogConsumer; import com.aliyun.openservices.log.flink.data.FastLogGroupDeserializer; import com.aliyun.openservices.log.flink.data.FastLogGroupList; import com.aliyun.openservices.log.flink.model.CheckpointMode; import com.aliyun.openservices.log.flink.util.Consts; import org.apache.flink.api.common.functions.FlatMapFunction; import org.apache.flink.api.java.utils.ParameterTool; import org.apache.flink.configuration.CheckpointingOptions; import org.apache.flink.configuration.Configuration; import org.apache.flink.runtime.state.filesystem.FsStateBackend; import org.apache.flink.streaming.api.CheckpointingMode; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.CheckpointConfig; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import java.util.Properties; public class FlinkConsumerSample { private static final String SLS_ENDPOINT = "your-endpoint"; // In this example, the AccessKey ID and AccessKey secret are obtained from environment variables. private static final String ACCESS_KEY_ID = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"); private static final String ACCESS_KEY_SECRET = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"); private static final String SLS_PROJECT = "your-project"; private static final String SLS_LOGSTORE = "your-logstore"; public static void main(String[] args) throws Exception { final ParameterTool params = ParameterTool.fromArgs(args); Configuration conf = new Configuration(); // Checkpoint dir like "file:///tmp/flink" conf.setString(CheckpointingOptions.CHECKPOINTS_DIRECTORY, "your-checkpoint-dir"); final StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment(1, conf); env.getConfig().setGlobalJobParameters(params); env.setParallelism(1); env.enableCheckpointing(5000); env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE); env.getCheckpointConfig().enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION); env.setStateBackend(new FsStateBackend("file:///tmp/flinkstate")); Properties configProps = new Properties(); configProps.put(ConfigConstants.LOG_ENDPOINT, SLS_ENDPOINT); configProps.put(ConfigConstants.LOG_ACCESSKEYID, ACCESS_KEY_ID); configProps.put(ConfigConstants.LOG_ACCESSKEY, ACCESS_KEY_SECRET); configProps.put(ConfigConstants.LOG_MAX_NUMBER_PER_FETCH, "10"); configProps.put(ConfigConstants.LOG_CONSUMER_BEGIN_POSITION, Consts.LOG_FROM_CHECKPOINT); configProps.put(ConfigConstants.LOG_CONSUMERGROUP, "your-consumer-group"); configProps.put(ConfigConstants.LOG_CHECKPOINT_MODE, CheckpointMode.ON_CHECKPOINTS.name()); configProps.put(ConfigConstants.LOG_COMMIT_INTERVAL_MILLIS, "10000"); FastLogGroupDeserializer deserializer = new FastLogGroupDeserializer(); DataStream<FastLogGroupList> stream = env.addSource( new FlinkLogConsumer<>(SLS_PROJECT, SLS_LOGSTORE, deserializer, configProps)); stream.flatMap((FlatMapFunction<FastLogGroupList, String>) (value, out) -> { for (FastLogGroup logGroup : value.getLogGroups()) { int logCount = logGroup.getLogsCount(); for (int i = 0; i < logCount; i++) { FastLog log = logGroup.getLogs(i); JSONObject jsonObject = new JSONObject(); jsonObject.put("topic", logGroup.getTopic()); jsonObject.put("source", logGroup.getSource()); for (int j = 0; j < log.getContentsCount(); j++) { jsonObject.put(log.getContents(j).getKey(), log.getContents(j).getValue()); } out.collect(jsonObject.toJSONString()); } } }).returns(String.class); stream.writeAsText("log-" + System.nanoTime()); env.execute("Flink consumer"); } }