<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Arunachalam Lakshmanan</title>
    <description>The latest articles on Forem by Arunachalam Lakshmanan (@arunx2).</description>
    <link>https://forem.com/arunx2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F433106%2F1297f872-aa31-4ec9-98b1-4fcc7a480dec.jpg</url>
      <title>Forem: Arunachalam Lakshmanan</title>
      <link>https://forem.com/arunx2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/arunx2"/>
    <language>en</language>
    <item>
      <title>Simple Go App to post messages to AWS SQS</title>
      <dc:creator>Arunachalam Lakshmanan</dc:creator>
      <pubDate>Tue, 08 Jun 2021 22:49:37 +0000</pubDate>
      <link>https://forem.com/arunx2/simple-go-app-to-post-messages-to-aws-sqs-4fnn</link>
      <guid>https://forem.com/arunx2/simple-go-app-to-post-messages-to-aws-sqs-4fnn</guid>
      <description>&lt;p&gt;AWS SQS: Amazon Simple Queue Service (SQS) is a fully managed message queuing service that enables you to decouple and scale microservices, distributed systems, and serverless applications. &lt;/p&gt;

&lt;p&gt;Some time we may need to send messages to AWS SQS along with message meta data. I use a simple program written in golang to post a message. &lt;/p&gt;

&lt;p&gt;As we do with all AWS programmatic access, we need to create session&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;sess&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSessionWithOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SharedConfigState&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SharedConfigEnable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to get the SQS queue url from the queue name&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetQueueURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetQueueUrlOutput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create an SQS service client&lt;/span&gt;
    &lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetQueueUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetQueueUrlInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;QueueName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can attach metadata to the message while posting it to the SQS queue&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;SendMsg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msgType&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queueURL&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create an SQS service client&lt;/span&gt;
    &lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendMessageInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MessageAttributes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MessageAttributeValue&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"msgType"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;StringValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msgType&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;StringValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;MessageBody&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;queueURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whole code can be found at github repo &lt;a href="https://github.com/arunx2/golang-samples/blob/main/publish-to-sqs/main.go"&gt;https://github.com/arunx2/golang-samples/blob/main/publish-to-sqs/main.go&lt;/a&gt; &lt;/p&gt;

</description>
      <category>go</category>
      <category>sqs</category>
    </item>
    <item>
      <title>Simple Slack Notification with golang</title>
      <dc:creator>Arunachalam Lakshmanan</dc:creator>
      <pubDate>Mon, 20 Jul 2020 02:43:05 +0000</pubDate>
      <link>https://forem.com/arunx2/simple-slack-notification-with-golang-55i2</link>
      <guid>https://forem.com/arunx2/simple-slack-notification-with-golang-55i2</guid>
      <description>&lt;p&gt;A slack notification is useful when&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A long-running job is started&lt;/li&gt;
&lt;li&gt;When some critical error&lt;/li&gt;
&lt;li&gt;A specific request comes through the process as cache invalidate, critical delete request, etc...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However slack provides simple webhooks to notify a channel, it has more features like action, workflow, poll, etc... So the slack go libraries need to comply with these features and that makes the library heavy by bringing internal dependencies. We can write a small tool in Go just for webhooks. It just uses standard libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a slack client with an optional user name one per process. The SendSlackNotification() can send a simple notification to the given channel.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Options:&lt;/em&gt; Username - the name of the bot in a slack message&lt;br&gt;
    Timeout - default would be 5 seconds&lt;br&gt;
    Icon_emoji - the name of the icon image, otherwise it is a default image of incoming webhook.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    sc := SlackClient{
        WebHookUrl: "https://WEB_HOOK_URL",
        UserName:   "USER_NAME",
        Channel:    "CHANNEL_NAME",
    }
    sr := SimpleSlackRequest{
        Text:      "This is test message",
        IconEmoji: ":ghost:",
    }
    err := sc.SendSlackNotification(sr)
    if err != nil {
        log.Fatal(err)
    }

    To send a notification with status (slack attachments)
    sr := SlackJobNotification{
        Text:  "This is attachment message",
        Details: "details of the jobs",
        Color: "warning",
        IconEmoji: ":hammer_and_wrench",
    }
    err := sc.SendJobNotification(sr)
    if err != nil {
        log.Fatal(err)
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package notification

import (
    "bytes"
    "encoding/json"
    "errors"
    "net/http"
    "strconv"
    "time"
)

const DefaultSlackTimeout = 5 * time.Second

type SlackClient struct {
    WebHookUrl string
    UserName   string
    Channel    string
    TimeOut    time.Duration
}

type SimpleSlackRequest struct {
    Text      string
    IconEmoji string
}

type SlackJobNotification struct {
    Color     string
    IconEmoji string
    Details   string
    Text      string
}

type SlackMessage struct {
    Username    string       `json:"username,omitempty"`
    IconEmoji   string       `json:"icon_emoji,omitempty"`
    Channel     string       `json:"channel,omitempty"`
    Text        string       `json:"text,omitempty"`
    Attachments []Attachment `json:"attachments,omitempty"`
}

type Attachment struct {
    Color         string `json:"color,omitempty"`
    Fallback      string `json:"fallback,omitempty"`
    CallbackID    string `json:"callback_id,omitempty"`
    ID            int    `json:"id,omitempty"`
    AuthorID      string `json:"author_id,omitempty"`
    AuthorName    string `json:"author_name,omitempty"`
    AuthorSubname string `json:"author_subname,omitempty"`
    AuthorLink    string `json:"author_link,omitempty"`
    AuthorIcon    string `json:"author_icon,omitempty"`
    Title         string `json:"title,omitempty"`
    TitleLink     string `json:"title_link,omitempty"`
    Pretext       string `json:"pretext,omitempty"`
    Text          string `json:"text,omitempty"`
    ImageURL      string `json:"image_url,omitempty"`
    ThumbURL      string `json:"thumb_url,omitempty"`
    // Fields and actions are not defined.
    MarkdownIn []string    `json:"mrkdwn_in,omitempty"`
    Ts         json.Number `json:"ts,omitempty"`
}

// SendSlackNotification will post to an 'Incoming Webook' url setup in Slack Apps. It accepts
// some text and the slack channel is saved within Slack.
func (sc SlackClient) SendSlackNotification(sr SimpleSlackRequest) error {
    slackRequest := SlackMessage{
        Text:      sr.Text,
        Username:  sc.UserName,
        IconEmoji: sr.IconEmoji,
        Channel:   sc.Channel,
    }
    return sc.sendHttpRequest(slackRequest)
}

func (sc SlackClient) SendJobNotification(job SlackJobNotification) error {
    attachment := Attachment{
        Color: job.Color,
        Text:  job.Details,
        Ts:    json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
    }
    slackRequest := SlackMessage{
        Text:        job.Text,
        Username:    sc.UserName,
        IconEmoji:   job.IconEmoji,
        Channel:     sc.Channel,
        Attachments: []Attachment{attachment},
    }
    return sc.sendHttpRequest(slackRequest)
}

func (sc SlackClient) SendError(message string, options ...string) (err error) {
    return sc.funcName("danger", message, options)
}

func (sc SlackClient) SendInfo(message string, options ...string) (err error) {
    return sc.funcName("good", message, options)
}

func (sc SlackClient) SendWarning(message string, options ...string) (err error) {
    return sc.funcName("warning", message, options)
}

func (sc SlackClient) funcName(color string, message string, options []string) error {
    emoji := ":hammer_and_wrench"
    if len(options) &amp;gt; 0 {
        emoji = options[0]
    }
    sjn := SlackJobNotification{
        Color:     color,
        IconEmoji: emoji,
        Details:   message,
    }
    return sc.SendJobNotification(sjn)
}
func (sc SlackClient) sendHttpRequest(slackRequest SlackMessage) error {
    slackBody, _ := json.Marshal(slackRequest)
    req, err := http.NewRequest(http.MethodPost, sc.WebHookUrl, bytes.NewBuffer(slackBody))
    if err != nil {
        return err
    }
    req.Header.Add("Content-Type", "application/json")
    if sc.TimeOut == 0 {
        sc.TimeOut = DefaultSlackTimeout
    }
    client := &amp;amp;http.Client{Timeout: sc.TimeOut}
    resp, err := client.Do(req)
    if err != nil {
        return err
    }

    buf := new(bytes.Buffer)
    _, err = buf.ReadFrom(resp.Body)
    if err != nil {
        return err
    }
    if buf.String() != "ok" {
        return errors.New("Non-ok response returned from Slack")
    }
    return nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>go</category>
      <category>devops</category>
    </item>
    <item>
      <title>Working with application logs</title>
      <dc:creator>Arunachalam Lakshmanan</dc:creator>
      <pubDate>Thu, 16 Jul 2020 02:54:09 +0000</pubDate>
      <link>https://forem.com/arunx2/working-with-application-logs-9g9</link>
      <guid>https://forem.com/arunx2/working-with-application-logs-9g9</guid>
      <description>&lt;p&gt;A small tutorial to work with application logs and analyze them with Elastic and Kibana. In a microservices application architecture, it is important to analyze the logs to understand the health of the application. We can extract several stats from the logs when a log is a strucApplication logs can be generated in multiple ways. They can be either simple logs or print to console (with no time stamps).&lt;/p&gt;

&lt;h2&gt;
  
  
  Collecting logs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log files&lt;/strong&gt; - Most applications default behaviour is to write the logs on the file system. They may rolled up by date or size. A simple &lt;a href="https://www.elastic.co/beats/filebeat"&gt;file beat&lt;/a&gt; process in the server can export the logs to either &lt;a href="https://www.elastic.co/logstash"&gt;logstash&lt;/a&gt; or &lt;a href="https://www.fluentd.org/"&gt;fluentd&lt;/a&gt;  or directly to &lt;a href="https://www.elastic.co/elasticsearch/"&gt;elastic&lt;/a&gt;.

&lt;ul&gt;
&lt;li&gt;Advantages: 

&lt;ul&gt;
&lt;li&gt;Simple file system&lt;/li&gt;
&lt;li&gt;Backups available as long as the file is purged.&lt;/li&gt;
&lt;li&gt;No code changes required.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Disadvantages:

&lt;ul&gt;
&lt;li&gt;As many as file exporter processes required to export the logs.&lt;/li&gt;
&lt;li&gt;May miss console outputs.&lt;/li&gt;
&lt;li&gt;In container environment, we need to create a volume for logs and mount them when spinning a container.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message brokers&lt;/strong&gt; - When writing logs into file system, there is a possibility that we need to clean up the disk space regularly. It can be cumbersome in containers (creating a volume and mount to containers). There are language specific log appenders available to push the logs directly from application. 

&lt;ul&gt;
&lt;li&gt;Advantages: 

&lt;ul&gt;
&lt;li&gt;No file system&lt;/li&gt;
&lt;li&gt;Log appenders can asynchronysly push logs to message brokers&lt;/li&gt;
&lt;li&gt;Messages can be dropped after TTL &lt;/li&gt;
&lt;li&gt;If the upstream processes are not accessible, logs won't be lost&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Disadvantages

&lt;ul&gt;
&lt;li&gt;Need infrastructure for message brokers.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log drivers&lt;/strong&gt; - This is more suitable option for legacy application where there are limitted accessability to code or change in application is forbidden. 

&lt;ul&gt;
&lt;li&gt;Advantages: 

&lt;ul&gt;
&lt;li&gt;No code change required.&lt;/li&gt;
&lt;li&gt;Everything that application emits including console prints.&lt;/li&gt;
&lt;li&gt;Several log drivers comes by default with docker runtime.&lt;/li&gt;
&lt;li&gt;No additional infrastructure.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Disadvantages:

&lt;ul&gt;
&lt;li&gt;There may be data lose with some protocols.&lt;/li&gt;
&lt;li&gt;Some log drivers can cause application hung when the upstream is not processing the data correctly or if not designed correctly.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Collecting logs via message brokers is most reliable option when considering failures in each layer even we need infrastructure for brokers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Best practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use JSON: Json format logs can be helpful rather than unstructured data. &lt;/li&gt;
&lt;li&gt;One log per function: Try to write one log function per exit point of the function. &lt;/li&gt;
&lt;li&gt;Check for isDebugEnabled: String concatenation is super expensive in any language. Do you message construction after checking the log level as most of the time the debug would be disabled.&lt;/li&gt;
&lt;li&gt;In microservices environment, keep the kafka topic name with some prefix. Just one process is sufficient in processing multiple topics.&lt;/li&gt;
&lt;li&gt;Keep index templates and enable index policies in Kibana. If logs needs to be preserved for long period of time, consider moving them to cold storage after certain period. We can still search them but stored in inexpensive storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sample logstash config
&lt;/h2&gt;

&lt;p&gt;Sample log stash configuration consuming messages from multiple topics of kafka and ingest into individual indices in elastic search.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input {
    kafka {
        codec =&amp;gt; "json"
        bootstrap_servers =&amp;gt; "broker1:9092,broker2:9092"
        topics_pattern =&amp;gt; "prefix-.*-logs"
        group_id =&amp;gt; "consumer_group_id"
        auto_offset_reset =&amp;gt; "earliest"
        decorate_events =&amp;gt; true
        metadata_max_age_ms =&amp;gt; 60000
        heartbeat_interval_ms =&amp;gt; "250000"
        session_timeout_ms =&amp;gt; "300000"
        poll_timeout_ms =&amp;gt; "30000"
        consumer_threads =&amp;gt; 6
    }
}

filter {
 kv {
     #default source field is message
     #source =&amp;gt; "message"
     field_split_pattern =&amp;gt; "([,\s]+)(?=\w+\s*\=)"
     transform_key =&amp;gt; "lowercase"
     recursive =&amp;gt; "true"
     trim_value =&amp;gt; "&amp;lt;&amp;gt;\[\],"

}
grok {
    match =&amp;gt; [ "message", "%{GREEDYDATA}" ]
}

mutate {
    #some languages emits json logs with double quotes. we need to change them to
    #single quote before parsing as json.
    gsub =&amp;gt; [
            "msg", '"{', "'{",
            "msg", '}"', "}'"
        ]
}
json {
    source =&amp;gt; "msg"
}
date {
    match =&amp;gt; ["[@metadata][kafka][timestamp]", "UNIX_MS"]
    target =&amp;gt;  "@timestamp"
   }
ruby {
    code =&amp;gt; "event.set('[@metadata][kafka][lc_topic]', event.get('[@metadata][kafka][topic]').split(/(?=[A-Z])/).map{|x| x.downcase }.join('_') )"
  }
}

output {
    elasticsearch{
        index =&amp;gt; "%{[@metadata][kafka][lc_topic]}-%{+YYYY.MM.dd}"
        hosts =&amp;gt; ["http://host1:9200", "http://host2:9200"]
    }
    #stdout {
        #codec =&amp;gt; rubydebug { metadata =&amp;gt; true }
    #}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

</description>
      <category>microservices</category>
      <category>elk</category>
      <category>logprocessing</category>
    </item>
  </channel>
</rss>
