<?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>DEV Community: Toshimitsu Takahashi</title>
    <description>The latest articles on DEV Community by Toshimitsu Takahashi (@tilfin).</description>
    <link>https://dev.to/tilfin</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%2F44035%2Ff8ddc109-ef85-42f8-9e4e-1e2f33cab39e.jpeg</url>
      <title>DEV Community: Toshimitsu Takahashi</title>
      <link>https://dev.to/tilfin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tilfin"/>
    <language>en</language>
    <item>
      <title>Seamless Local Development with MFA-Enabled Roles on AWS</title>
      <dc:creator>Toshimitsu Takahashi</dc:creator>
      <pubDate>Mon, 03 Jun 2024 15:10:23 +0000</pubDate>
      <link>https://dev.to/tilfin/seamless-local-development-with-mfa-enabled-roles-on-aws-89i</link>
      <guid>https://dev.to/tilfin/seamless-local-development-with-mfa-enabled-roles-on-aws-89i</guid>
      <description>&lt;p&gt;This article is a translation of &lt;a href="https://zenn.dev/tilfin/articles/ab53ae77a8378f"&gt;https://zenn.dev/tilfin/articles/ab53ae77a8378f&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Many of us have an AWS account for our main login portal and separate AWS accounts for each project. We often need to assume roles to obtain temporary credentials and access resources like S3 and DynamoDB on AWS from our local development environment.&lt;/p&gt;

&lt;p&gt;The challenge lies in the short-lived nature of these credentials. In a development environment where the server automatically reloads upon code changes, manually renewing credentials and restarting the server every hour becomes a tedious task. This becomes even more cumbersome with MFA (Multi-Factor Authentication), requiring manual one-time password entry.&lt;/p&gt;

&lt;p&gt;This article outlines the steps I took to streamline this process and enable seamless development without interruptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Temporary Credential Issuance
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;aws sts assume-role&lt;/code&gt; is the standard AWS CLI command for this purpose, I've developed a tool called &lt;code&gt;swrole&lt;/code&gt; to simplify this process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tilfin/homebrew-aws/tree/master?tab=readme-ov-file#swrole"&gt;https://github.com/tilfin/homebrew-aws/tree/master?tab=readme-ov-file#swrole&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  swrole 1.1 Release
&lt;/h2&gt;

&lt;p&gt;This latest update introduces multiple ways to handle one-time passwords (OTP). While version 1.0 only supported interactive input, version 1.1 allows you to pass the OTP directly as a command argument using &lt;code&gt;-t 123456&lt;/code&gt;. Additionally, you can now define &lt;code&gt;generate_token&lt;/code&gt; within the target profile section of your &lt;code&gt;~/.aws/config&lt;/code&gt; file. This enables automatic OTP retrieval from services like 1Password.&lt;/p&gt;

&lt;h3&gt;
  
  
  ~/.aws/credentials
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[my-company]&lt;/span&gt;
&lt;span class="py"&gt;aws_access_key_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;XXXXXXXXXX&lt;/span&gt;
&lt;span class="py"&gt;aws_secret_access_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;XXXXXXXXXX&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ~/.aws/config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile my-company]&lt;/span&gt;
&lt;span class="py"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ap-northeast-1&lt;/span&gt;

&lt;span class="nn"&gt;[profile project-dev]&lt;/span&gt;
&lt;span class="py"&gt;role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::111111111111:role/developer&lt;/span&gt;
&lt;span class="py"&gt;mfa_serial&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::000000000000:mfa/member&lt;/span&gt;
&lt;span class="py"&gt;source_profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;my-company&lt;/span&gt;
&lt;span class="py"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ap-northeast-1&lt;/span&gt;
&lt;span class="py"&gt;generate_token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;op item get "AWS Account" --otp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;strong&gt;generate_token&lt;/strong&gt; is configured to fetch the OTP for "AWS Account" using the &lt;a href="https://developer.1password.com/docs/cli/"&gt;1Password CLI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging Process Credential Provider in AWS SDKs
&lt;/h2&gt;

&lt;p&gt;During my research, I discovered the &lt;code&gt;credential_process&lt;/code&gt; configuration option in &lt;code&gt;~/.aws/config&lt;/code&gt;. This allows AWS SDKs to execute a specified command and utilize the resulting JSON output (from standard output) as credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/sdkref/latest/guide/feature-process-credentials.html"&gt;https://docs.aws.amazon.com/sdkref/latest/guide/feature-process-credentials.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recognizing its potential, I've incorporated this functionality into &lt;strong&gt;swrole&lt;/strong&gt; version 1.1. Using the &lt;code&gt;-j&lt;/code&gt; option now outputs the credential JSON in the format expected by the Process credential provider.&lt;/p&gt;

&lt;p&gt;However, directly using &lt;strong&gt;credential_process&lt;/strong&gt; with the &lt;code&gt;project-dev&lt;/code&gt; profile won't work seamlessly due to the SDK's credential prioritization. We need to define a separate profile specifically for application development.&lt;/p&gt;

&lt;h3&gt;
  
  
  ~/.aws/config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile app-dev]&lt;/span&gt;
&lt;span class="py"&gt;credential_process&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;swrole -j project-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ~/.aws/credentials
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[app-dev]&lt;/span&gt;
&lt;span class="py"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ap-northeast-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While other SDKs are untested, using &lt;code&gt;~/.aws/credentials&lt;/code&gt; for &lt;code&gt;credential_process&lt;/code&gt; works with AWS SDK JavaScript v3.&lt;/p&gt;

&lt;p&gt;Now, you can run your development server with this AWS profile, triggering the seamless credential fetching process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ AWS_PROFILE=app-dev pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Ensure your AWS SDK utilizes proper credential caching to avoid issues with concurrent requests using the same one-time password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Notes
&lt;/h2&gt;

&lt;p&gt;I encountered some challenges with AWS SDK JavaScript v3's credential caching behavior related to this approach. You can find a detailed explanation in this article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/tilfin/optimizing-credential-configuration-in-aws-sdk-for-javascript-v3-understanding-cache-mechanisms-and-best-practices-oka"&gt;Optimizing Credential Configuration in AWS SDK for JavaScript v3: Understanding Cache Mechanisms and Best Practices&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, this method eliminates the need for manual OTP input even with the AWS CLI itself. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>localdev</category>
    </item>
    <item>
      <title>Optimizing Credential Configuration in AWS SDK for JavaScript v3: Understanding Cache Mechanisms and Best Practices</title>
      <dc:creator>Toshimitsu Takahashi</dc:creator>
      <pubDate>Mon, 03 Jun 2024 15:04:49 +0000</pubDate>
      <link>https://dev.to/tilfin/optimizing-credential-configuration-in-aws-sdk-for-javascript-v3-understanding-cache-mechanisms-and-best-practices-oka</link>
      <guid>https://dev.to/tilfin/optimizing-credential-configuration-in-aws-sdk-for-javascript-v3-understanding-cache-mechanisms-and-best-practices-oka</guid>
      <description>&lt;p&gt;This article is a translation of &lt;a href="https://zenn.dev/tilfin/articles/56f8dc56b83901"&gt;https://zenn.dev/tilfin/articles/56f8dc56b83901&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;The AWS SDK for JavaScript v3 introduced a significant shift in client configuration compared to v2. While v2 allowed for global SDK settings, v3 adopts a per-client instance runtime configuration approach. Consequently, credential retrieval, which was internally cached in v2 even with randomly generated clients for each AWS resource, now presents potential challenges in v3 with frequent credential requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delving into Credential Retrieval Logic
&lt;/h2&gt;

&lt;p&gt;The simplest approach involves directly setting AWS access and secret keys as &lt;code&gt;credentials&lt;/code&gt; in the constructor arguments of AWS resource clients. However, this method is not ideal for real-world application development. Applications deployed on EC2 or ECS typically retrieve credentials from instance or container metadata services, while Lambda functions often obtain them via environment variables. The AWS SDK v3 implements these credential supply logics as &lt;strong&gt;Credential Providers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can also specify a &lt;strong&gt;Credential Provider&lt;/strong&gt; for the client's &lt;code&gt;credentials&lt;/code&gt;. Details are described on the following page:&lt;br&gt;
&lt;a href="https://github.com/aws/aws-sdk-js-v3/tree/main/packages/credential-providers"&gt;https://github.com/aws/aws-sdk-js-v3/tree/main/packages/credential-providers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If nothing is specified for &lt;code&gt;credentials&lt;/code&gt;, the function set in &lt;code&gt;credentialDefaultProvider&lt;/code&gt; is executed first to generate a &lt;strong&gt;Credential Provider&lt;/strong&gt;, which is then used as &lt;code&gt;credentials&lt;/code&gt;. The default &lt;code&gt;credentialDefaultProvider&lt;/code&gt; is &lt;code&gt;defaultProvider&lt;/code&gt;. It attempts various acquisition methods. By using this method, you can automatically refer to &lt;code&gt;~/.aws/credentials&lt;/code&gt; in a local environment and metadata in a deployed environment.&lt;/p&gt;

&lt;p&gt;To explicitly specify the implementation of &lt;code&gt;defaultProvider&lt;/code&gt;, use &lt;code&gt;fromNodeProviderChain&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Credential Caching Mechanism
&lt;/h2&gt;

&lt;p&gt;Credential retrieval occurs each time a client invokes a command. However, &lt;strong&gt;Credential Providers, which implement the various retrieval logics provided by the AWS SDK, do not have a caching mechanism. The only exception is the &lt;code&gt;memoize&lt;/code&gt; function of &lt;code&gt;@smithy/property-provider&lt;/code&gt; used internally by &lt;code&gt;defaultProvider&lt;/code&gt;.&lt;/strong&gt; Furthermore, &lt;strong&gt;the re-acquisition logic for time-limited credentials, including session tokens, is also implemented only here.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aws/aws-sdk-js-v3/blob/main/packages/credential-provider-node/src/defaultProvider.ts#L59"&gt;https://github.com/aws/aws-sdk-js-v3/blob/main/packages/credential-provider-node/src/defaultProvider.ts#L59&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/smithy-lang/smithy-typescript/blob/main/packages/property-provider/src/memoize.ts#L46"&gt;https://github.com/smithy-lang/smithy-typescript/blob/main/packages/property-provider/src/memoize.ts#L46&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What are the Best Practices?
&lt;/h2&gt;

&lt;p&gt;Based on the insights so far, in most cases where you are implementing an application that operates within the same AWS account, "using &lt;code&gt;defaultProvider&lt;/code&gt;", or "specifying nothing", is the best approach. However, there is a potential pitfall here.&lt;/p&gt;

&lt;p&gt;As mentioned at the beginning, in v3, since each client has a different &lt;strong&gt;Credential Provider&lt;/strong&gt;, caching is also done on a per-client basis. For example, in a batch processing loop where the code "creates a client and retrieves an item", there is a possibility that credential acquisition will fail. Below is an issue where an error occurred because the access limit of EC2's metadata endpoint was reached.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aws/aws-sdk-js-v3/issues/4867"&gt;https://github.com/aws/aws-sdk-js-v3/issues/4867&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a workaround, the issue's comments suggest caching (reusing) the instances of each AWS client themselves. However, in applications that rely on many AWS services and where command transmissions to them occur intensively, this may not be avoidable. There is also an improvement proposal for this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aws/aws-sdk-js-v3/issues/4612"&gt;https://github.com/aws/aws-sdk-js-v3/issues/4612&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Currently, the best practice is to reuse &lt;code&gt;defaultProvider&lt;/code&gt;. Since &lt;code&gt;fromNodeProviderChain&lt;/code&gt; is provided by the SDK, generate it at the module level as shown below and reuse it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { fromNodeProviderChain } from "@aws-sdk/credential-providers";

export const credentialProvider = fromNodeProviderChain();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new S3Client({ credentials: credentialProvider });

new DynamoDBClient({ credentials: credentialProvider });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will prevent the retrieval process from being concentrated internally.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://zenn.dev/luma/articles/bd3c59b3d7682d"&gt;https://zenn.dev/luma/articles/bd3c59b3d7682d&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>aws</category>
      <category>awssdk</category>
    </item>
    <item>
      <title>The recommendation of structured logging and the reason I have made Ougai for Ruby</title>
      <dc:creator>Toshimitsu Takahashi</dc:creator>
      <pubDate>Sun, 20 May 2018 14:14:07 +0000</pubDate>
      <link>https://dev.to/tilfin/the-recommendation-of-structured-logging-and-the-reason-i-have-made-ougai-for-ruby-39pn</link>
      <guid>https://dev.to/tilfin/the-recommendation-of-structured-logging-and-the-reason-i-have-made-ougai-for-ruby-39pn</guid>
      <description>&lt;h2&gt;
  
  
  Structured logging
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;structured logging&lt;/strong&gt; is to make a log that is easy to process mechanically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison of usual logs and structured logs
&lt;/h2&gt;

&lt;p&gt;The usual logs are basically just &lt;em&gt;timestamps&lt;/em&gt;, &lt;em&gt;level&lt;/em&gt;, and &lt;em&gt;message strings&lt;/em&gt;. Information to be the context of events (events) to be kept as logs is appropriately embedded in messages. It is easy for human to read on a console.&lt;/p&gt;

&lt;p&gt;In the structured log, information which becomes the context embedded in the message is independently given to the field of the log structure. Therefore, it is easier to analyze later. And the output logs are often text based JSON.&lt;/p&gt;

&lt;p&gt;Let's compare this. The log of normal Ruby Logger and my own Ougai will be as follows. It is a log that "user created article" (articles shaved).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"User created article  (user_id=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; article_id=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I, [2018-05-13T17:51:08.772542 #6253]  INFO -- : User created article  (user_id=123 article_id=45)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s2"&gt;"User created article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;article_id: &lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"pid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6253&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2018-05-13T17:52:25.147+09:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"User created article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"article_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;※ It differs from the default format&lt;/p&gt;

&lt;p&gt;As you can see, you have to embed the normal logs and convert the incidental information into strings. On the other hand, structured logs are longer by JSON effect, so it is difficult to read in the console. However, it is not a problem if you use log viewer with switchover in the operating environment of the formatter or parse mechanism.&lt;/p&gt;

&lt;p&gt;Structured logs are easier to analyze is that, for example, if you want to extract only the logs of certain users, simply &lt;code&gt;grep "user_id=10"&lt;/code&gt; will cause to hook entries whose user_id is like &lt;code&gt;101&lt;/code&gt; in normal logs. On the other hand structured logs (mainly using JSONPath) can be easily and reliably filtered by like a &lt;code&gt;$.user_id = 10&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Log management service and structured log
&lt;/h2&gt;

&lt;p&gt;SaaS for log management and analysis such as &lt;a href="https://aws.amazon.com/jp/cloudwatch/" rel="noopener noreferrer"&gt;AWS CloudWatch Logs&lt;/a&gt;, &lt;a href="https://cloud.google.com/logging/" rel="noopener noreferrer"&gt;Stackdriver Logging&lt;/a&gt; on Google Cloud and &lt;a href="https://www.loggly.com/" rel="noopener noreferrer"&gt;Loggly&lt;/a&gt;, &lt;a href="https://logentries.com/" rel="noopener noreferrer"&gt;Logentries&lt;/a&gt; all support structured logs, or you can not make use of those services unless you do. However, since the library itself provided by each service does not correspond to the structured output, I think that it is best to relay by Fluentd and send it by present situation best.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reason for making Ougai
&lt;/h2&gt;

&lt;p&gt;Originally I wrote the front API server in Node.js and participated frequently in the project to create backend management service with Ruby / Rails. Node.js had a famous JSON logger named &lt;a href="https://github.com/trentm/node-bunyan" rel="noopener noreferrer"&gt;Bunyan&lt;/a&gt;, and I liked it. But Ruby has libraries that handle several structured logs, but each library was as a framework. Node.js itself has functions for console output like &lt;code&gt;console.info&lt;/code&gt;, but Ruby has its own Logger, though. In another micro service, each service is often implemented in multiple languages, but if possible, I wanted to unify the format in order to analyze the log cross-sectionally.&lt;/p&gt;

&lt;p&gt;With these backgrounds, I built my own logger based on Ruby's original Logger, which is a logger that has the Bunyan compatible log output format described above. That is Ougai. (I named Ougai from a Japanese literary person after Bunyan was named from the English literary.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tilfin/ougai" rel="noopener noreferrer"&gt;GitHub: tilfin/Ougai - A Ruby structured logging is capable of handling a message, custom data or an exception easily and generates JSON or human readable logs.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since it is an extension of Ruby's original Logger class, even if it is introduced suddenly, it does not crash simply by appearing in the message field. You can gradually move the context to an independent field. The format is standard, for JSON output it is &lt;a href="https://github.com/pinojs/pino" rel="noopener noreferrer"&gt;Pino&lt;/a&gt; compatible with Bunyan of Node.js, &lt;strong&gt;Readable&lt;/strong&gt; which is colorfully easy to read at the terminal using &lt;a href="https://github.com/awesome-print/awesome_print" rel="noopener noreferrer"&gt;awesome_print&lt;/a&gt; is included. Of course for JSON you can browse with dedicated log viewer commands owned by Bunyan and Pino respectively. There is also a function to make common information easy to put in by hook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tilfin/ougai/wiki" rel="noopener noreferrer"&gt;https://github.com/tilfin/ougai/wiki&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The wiki also contains examples of settings to use it in combination with Rails, Sidekiq, Fluentd etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rubygems.org/gems/ougai" rel="noopener noreferrer"&gt;https://rubygems.org/gems/ougai&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>logging</category>
      <category>programming</category>
    </item>
    <item>
      <title>Transgate is Agent-based taskflow framework for Node.js</title>
      <dc:creator>Toshimitsu Takahashi</dc:creator>
      <pubDate>Thu, 23 Nov 2017 12:56:09 +0000</pubDate>
      <link>https://dev.to/tilfin/transgate-is-agent-based-taskflow-framework-for-nodejs-58b</link>
      <guid>https://dev.to/tilfin/transgate-is-agent-based-taskflow-framework-for-nodejs-58b</guid>
      <description>&lt;p&gt;I made &lt;a href="https://github.com/tilfin/transgate" rel="noopener noreferrer"&gt;Transgate&lt;/a&gt; Agent-based taskflow framework in Node.js .&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did I make it?
&lt;/h2&gt;

&lt;p&gt;Because the various flows got messed up during I wrote a program to operate my home appliances. They are to obtain temperature and humidity from dyson cool fan regularly, to save it in the database, and to handle messages coming from Google Home / Assistant + IFTTT and operate &lt;a href="http://getirkit.com/" rel="noopener noreferrer"&gt;IRKit&lt;/a&gt;. According to the temperature, I also wanted to automatically operate the air conditioner via IRKit. How do you write them?&lt;/p&gt;

&lt;h2&gt;
  
  
  What thing？
&lt;/h2&gt;

&lt;p&gt;Suddenly I want you to imagine sorting of baggage such as airport. The agent receives items from the gate, processes them, and sends them to another gate. The agent does not know what is going on the other side of the gate. When the agent comes up with an empty item, it finishes the work. The image of the framework architecture looks like this.&lt;/p&gt;

&lt;p&gt;The agent can receive items from the gate and send new items to another gate. Item is a simple Object. An agent can concentrate on its own task. So even if the number of previous process or next process increases or decreases, it moves without problems if the schema of the item does not change. And input / output is simple so unit testing is easy. Since the agent does not know the substance of the gate, it can easily be replaced with the input source gate as the stub and the output destination gate as the mock.&lt;/p&gt;

&lt;h2&gt;
  
  
  Actors in this framework
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gate&lt;/strong&gt; is an endpoint of Input/Output. For example, file storage, database, queue or API service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent&lt;/strong&gt; is a worker to process an item between Input/Output gates and does not know anything opposite gates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Item&lt;/strong&gt; is an entity as each task target, to be exchanged between gates, and an Object or a JSON. &lt;code&gt;null&lt;/code&gt; indicates the terminator.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  An example
&lt;/h2&gt;

&lt;p&gt;Let's explain through the home control program that triggered making this framework. By the way this program is running as a daemon on Raspberry PI in my shoebox.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flow diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwb1tbkotb68462ng0lc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwb1tbkotb68462ng0lc.png" alt="Flow diagram" width="723" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Main program (main.js)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;HttpClientGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;HttpServerGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;IntervalGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;JointGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;StdoutGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;duplicator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mixer&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="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transgate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pino&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pino&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;konfig-yaml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MongoGate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/mongo_gate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;IRKitGate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/irkit_gate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Agent&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AnalysisCommander&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/analysis_commander&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DysonCoolLinkRecorder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/dyson/cool_link_recorder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EnvironmentalAnalyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/environmental_analyzer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Gate&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slackGate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpClientGate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhook_url&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iftttGate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpServerGate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;irkitGate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IRKitGate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;irkit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intervalGate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntervalGate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoGate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MongoGate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mongodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mongodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;drToEaGate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JointGate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AnalysisCommander&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iftttGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;irkitGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slackGate&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DysonCoolLinkRecorder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intervalGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;duplicator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mongoGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;drToEaGate&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EnvironmentalAnalyzer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;drToEaGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;irkitGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slackGate&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="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pino&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;iftttGate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mongoGate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;intervalGate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&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="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;pino&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;h3&gt;
  
  
  7 Gates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;slackGate&lt;/strong&gt; posts a text message to slack. Even if it is not specially implemented, it will be an instance of HttpClientGate. The item JSON is &lt;code&gt;{ "text": "&amp;lt;text message&amp;gt;" }&lt;/code&gt; 。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iftttGate&lt;/strong&gt; uses JSON received from IFTTT's webhook as an item. The item JSON is &lt;code&gt;{ "target": "TV", "text": "&amp;lt;speaking words&amp;gt;" }&lt;/code&gt; 。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;irkitGate&lt;/strong&gt; instructs infrared transmitter with HTTP interface. The item JSON is &lt;code&gt;{ "command": "celling_light_off" }&lt;/code&gt; 。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;intervalGate&lt;/strong&gt; creates items at regular intervals. Item is &lt;code&gt;{" time ": &amp;lt;Date instance&amp;gt;}&lt;/code&gt;. In this case run agent processing every minute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mongoGate&lt;/strong&gt; registers items sent to MongoDB's designated collection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;drToEaGate&lt;/strong&gt; is a joint flow of items from &lt;strong&gt;DysonCoolLinkRecorder&lt;/strong&gt; (described later) to &lt;strong&gt;EnvironmentalAnalyzer&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3 Agents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AnalysisCommander&lt;/strong&gt; receives JSON from the IFTTT webhook as an item and specifies the infrared signal to be sent to IRKit from the operation target and text. Post it when slack can not interpret wording.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DysonCoolLinkRecorder&lt;/strong&gt; gets temperature and humidity from the Dyson PureCoolLink fan every 1 minute and sends it to the gate which becomes the joint and write to MongoDB across the duplicator.&lt;/li&gt;
&lt;li&gt;When &lt;strong&gt;EnvironmentalAnalyzer&lt;/strong&gt; exceeds the threshold value from the temperature through the joint, it requests IRKit to operate the air conditioner. When you operate automatically, record it in slack.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Agent implementation
&lt;/h3&gt;

&lt;p&gt;Create a subclass of &lt;strong&gt;Agent&lt;/strong&gt;. Write the code of processing the item on received in the main method and sending a new item to the specified gate. We use the before / after hook method to control (start / stop) the initialization process and another process (eg headless chrome) here.&lt;/p&gt;

&lt;p&gt;The following is an implementation example of &lt;strong&gt;EnvironmentalAnalyzer&lt;/strong&gt;. When the room temperature becomes 17 degrees Celsius degree or less, turn on the air conditioner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Agent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transgate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnvironmentalAnalyzer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_preTemp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_airconAlive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;irkitGate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slackGate&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;curTemp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_preTemp&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_preTemp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;curTemp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;17&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="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_airconAlive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;irkitGate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aircon_on&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_airconAlive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;slackGate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Turn on aircon because temp is down to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;curTemp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_preTemp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;curTemp&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason why the constructor and the input gate are concealed is for the implementation of the specification that when the agent receives null, it sends null to the next gate and ends himself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Suitable for complex daemons and batch programs.&lt;/li&gt;
&lt;li&gt;Not suitable for processing large volume because it is not assumed that the same agent is running in parallel.&lt;/li&gt;
&lt;li&gt;Gates and agents that appear and the task flow of items can be defined in the main program. Therefore, the whole can be grasped by only it.&lt;/li&gt;
&lt;li&gt;Processing of agents can be written in sync with async / await in a pseudo manner. Even if the number of agents increases, they will not be heavy like thread-based.&lt;/li&gt;
&lt;li&gt;Since it is easy to replace the gate, it is easy to write unit tests of the agent and to confirm partial execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Predicting answers to questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Will the referenced services be all gates?
&lt;/h3&gt;

&lt;p&gt;It's no. Between gates is limited to one way. The agent does not know the detail specification of the gate. In other words, you can not throw a request and get a response to it. It is possible to loop, not round-trip, The response is determined for the request to send out because it is stateless. The gate becomes the part that  the trigger to the agent and the part where the agent sends the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to inform the kicker when the series of the flow is over?
&lt;/h3&gt;

&lt;p&gt;The queue system needs to send a completion notice when the task is completed. In such a case, you can turn the flow by giving the item its context. Make sure the last gate is responsible for sending completion notification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should the logger be a gate?
&lt;/h3&gt;

&lt;p&gt;If logs are the outputs themselves, they should be gated. Then you can easily replace the gate with something that will be jointed to the Agent later and then throw it to the log analysis service from there.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much logic can we include in the gate?
&lt;/h3&gt;

&lt;p&gt;The gate should be as simple as possible. The agent is designed to make it easier to test. But if you put the logic in the gate itself, you can not replace the input / output destination and test it. However, if it is simple and the common logic in the project, it may be implemented in the gate. If it is complicated, you should make an agent for it, connect the agents by a joint gate.&lt;/p&gt;

&lt;p&gt;I would be pleased if you are interested in &lt;a href="https://github.com/tilfin/transgate" rel="noopener noreferrer"&gt;Transgate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://tilfin.hatenablog.com/entry/2017/11/23/210540" rel="noopener noreferrer"&gt;Japanese version&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>framework</category>
    </item>
  </channel>
</rss>
