All Products
Search
Document Center

Simple Log Service:Consume data using Flink

Last Updated:Aug 26, 2026

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

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>
Note

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

setProject(String project)

Yes

N/A

The Simple Log Service project to consume.

setLogStore(String logstore)

Yes

N/A

The Logstore to consume.

setEndpoint(String endpoint)

Yes

N/A

The Simple Log Service endpoint, for example, cn-hangzhou.log.aliyuncs.com.

setCredentials(String accessKeyId, String accessKey)

Yes

N/A

The AccessKey ID and AccessKey secret used to access Simple Log Service.

setDeserializer(AliyunLogDeserializationSchema<T> deserializer)

Yes

N/A

The deserializer that converts Simple Log Service pull results into Flink records.

setConsumerGroup(String consumerGroup)

No

N/A

The Simple Log Service consumer group name. Used to read or commit server-side checkpoints.

setStartingPosition(StartingPosition)

No

earliest

The starting position for consumption. Supported values: earliest, latest, checkpoint, or a Unix timestamp in seconds.

setFallbackPosition(StartingPosition)

No

earliest

The fallback position used when the starting position is checkpoint and no server-side checkpoint exists.

ConfigConstants.LOG_MAX_NUMBER_PER_FETCH

No

100

The maximum number of LogGroups pulled from a single shard per request.

ConfigConstants.LOG_FETCH_DATA_INTERVAL_MILLIS

No

100

The wait interval in milliseconds before the next pull when no data is returned.

ConfigConstants.LOG_SHARDS_DISCOVERY_INTERVAL_MILLIS

No

60000

The polling interval in milliseconds for detecting shard splits or merges.

ConfigConstants.LOG_CHECKPOINT_MODE

No

ON_CHECKPOINTS

The server-side checkpoint commit mode. ON_CHECKPOINTS: commits when a Flink checkpoint completes. PERIODIC: commits at a separate interval. DISABLED: does not commit to the server.

ConfigConstants.STOP_TIME

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.

ConfigConstants.MAX_RETRIES

No

5

The maximum number of retries for general errors.

ConfigConstants.SIGNATURE_VERSION

No

v1

The request signature version. Valid values: v1 and v4. If you use v4, you must also set REGION_ID.

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

setProject(String project)

Yes

N/A

The target project for writing.

setLogStore(String logstore)

Yes

N/A

The default target Logstore for writing. This value is overridden when a SinkRecord specifies a different Logstore.

setEndpoint(String endpoint)

Yes

N/A

The Simple Log Service endpoint.

setCredentials(String accessKeyId, String accessKey)

Yes

N/A

The AccessKey ID and AccessKey secret used to access Simple Log Service.

setSerializer(AliyunLogSerializationSchema<T> serializer)

Yes

N/A

The serializer that converts Flink records into SinkRecord objects.

ConfigConstants.FLUSH_INTERVAL_MS

No

Producer SDK default

The maximum time in milliseconds that logs are cached on the client before being sent.

ConfigConstants.MAX_RETRIES

No

Producer SDK default

The maximum number of retries for failed send operations.

ConfigConstants.IO_THREAD_NUM

No

Producer SDK default

The number of I/O threads used to send logs.

ConfigConstants.TOTAL_SIZE_IN_BYTES

No

Producer SDK default

The total cache size available to the Producer client.

ConfigConstants.MAX_BLOCK_TIME_MS

No

Producer SDK default

The maximum time in milliseconds that a send call blocks when the cache is full or resources are insufficient.

ConfigConstants.SIGNATURE_VERSION

No

v1

The request signature version. Valid values: v1 and v4. If you use v4, you must also set REGION_ID.

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

connector

Source / Sink

Yes

N/A

Fixed value: aliyun-log.

endpoint

Source / Sink

Yes

N/A

The Simple Log Service endpoint.

project

Source / Sink

Yes

N/A

The Simple Log Service project.

logstore

Source / Sink

Yes

N/A

The Logstore to read from (Source) or the default Logstore to write to (Sink).

access.key.id

Source / Sink

Yes

N/A

The AccessKey ID used to access Simple Log Service.

access.key.secret

Source / Sink

Yes

N/A

The AccessKey secret used to access Simple Log Service.

consumer-group

Source

No

N/A

The consumer group name. Used to read or commit server-side checkpoints.

scan.startup.mode

Source

No

earliest

The starting position for consumption. Valid values: earliest, latest, checkpoint, or a Unix timestamp in seconds.

checkpoint.mode

Source

No

on-checkpoints

The server-side checkpoint commit mode. Valid values: on-checkpoints, periodic, and disabled.

max.number.per.fetch

Source

No

100

The maximum number of LogGroups pulled from a single shard per request.

ignore-parse-errors

Source

No

false

Specifies whether to output NULL when field type conversion fails. A value of false throws an exception.

sink.topic

Sink

No

""

The default LogGroup topic used for writes. This value can be overridden by the __topic__ column.

sink.source

Sink

No

N/A

The default LogGroup source used for writes. This value can be overridden by the __source__ column.

flush.interval.ms

Sink

No

Producer SDK default

The maximum time that logs are cached on the client before being sent.

signature.version

Source / Sink

No

v1

The request signature version. Valid values: v1 and v4.

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

__time__

TIMESTAMP(3)

The Simple Log Service log time.

__topic__

STRING

The LogGroup topic.

__source__

STRING

The LogGroup source.

__shard__

INT

The shard ID of the current record.

__cursor__

STRING

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

__time__

TIMESTAMP(3)

The Simple Log Service log time. A timestamp type is written in seconds.

__topic__

STRING

Overrides sink.topic to set the topic of the current record.

__source__

STRING

Overrides sink.source to set the source of the current record.

__logstore__

STRING

Overrides the logstore table parameter to write the current record to the specified Logstore.

__hash_key__

STRING

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

log:GetCursorOrData

acs:log:${regionName}:${projectOwnerAliUid}:project/${projectName}/logstore/${logstoreName}

log:ListShards

acs:log:${regionName}:${projectOwnerAliUid}:project/${projectName}/logstore/${logstoreName}

log:CreateConsumerGroup

acs:log:${regionName}:${projectOwnerAliUid}:project/${projectName}/logstore/${logstoreName}/consumergroup/*

log:ConsumerGroupUpdateCheckPoint

acs:log:${regionName}:${projectOwnerAliUid}:project/${projectName}/logstore/${logstoreName}/consumergroup/${consumerGroupName}

Permissions required for Sink writes

API

Resource

log:PostLogStoreLogs

acs:log:${regionName}:${projectOwnerAliUid}:project/${projectName}/logstore/${logstoreName}

Flink Log Consumer

Important

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_MILLIS and ConfigConstants.LOG_MAX_NUMBER_PER_FETCH to 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.

  1. Configure startup parameters.

    The following example uses java.util.Properties for configuration. All parameters are defined in the ConfigConstants class.

    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.
                }
            }
        }
    });
    Note

    The 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.

  2. Set the start position for consumption.

    Set ConfigConstants.LOG_CONSUMER_BEGIN_POSITION to 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. Use ConfigConstants.LOG_CONSUMERGROUP to 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);
    Note

    If Flink recovers from its own StateBackend, these settings are ignored and consumption resumes from the StateBackend checkpoint.

  3. 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");
    Note

    If 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.

  4. 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

Important

Legacy API. Planned for removal. Use AliyunLogSink for new jobs.

The Flink Log Producer writes data to Simple Log Service.

Note

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

  1. 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_RETRIES

    Override LogSerializationSchema and define a method to serialize data into a RawLogGroup.

    A RawLogGroup is a collection of logs. For more information about the fields, see Log.

    To write data to a specific shard, use LogPartitioner to 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";
          }
      });
  2. 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");
    }
}