<?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: Gernot Glawe</title>
    <description>The latest articles on Forem by Gernot Glawe (@megaproaktiv).</description>
    <link>https://forem.com/megaproaktiv</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%2F184411%2Fec0489ba-df69-481e-8d2a-f837a2a9b65e.png</url>
      <title>Forem: Gernot Glawe</title>
      <link>https://forem.com/megaproaktiv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/megaproaktiv"/>
    <language>en</language>
    <item>
      <title>LAPD - Lambda Python Deployment for special "get out of jail" cases</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Sun, 10 Dec 2023 18:45:46 +0000</pubDate>
      <link>https://forem.com/aws-builders/lapd-lambda-python-deployment-for-special-get-out-of-jail-cases-4kgl</link>
      <guid>https://forem.com/aws-builders/lapd-lambda-python-deployment-for-special-get-out-of-jail-cases-4kgl</guid>
      <description>&lt;p&gt;Sometimes you develop with a framework that is restrictive, or an environment which is event more restricted.&lt;/p&gt;

&lt;p&gt;Get out of jail with zipping your own Lambda package.&lt;/p&gt;

&lt;p&gt;Without packaging requirements.txt each time, but just adding it to the zip you wayyy faster. So fast High Speed Police will come.&lt;/p&gt;

&lt;p&gt;Did you know you can also download the code from the lambda function itself? Then you got also the correct dependencies. Because sometimes if C libraries involved, it has to be linux. And not everybodys Laptop is Linux, is it? :) &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/megaproaktiv/lapd"&gt;https://github.com/megaproaktiv/lapd&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So enjoy this little helper, you might not need it. But in a special project it saved me a lot of time.&lt;/p&gt;

&lt;p&gt;Because its &lt;a href="https://www.go-on-aws.com/"&gt;GO&lt;/a&gt;, you can just copy your binary from &lt;a href="https://github.com/megaproaktiv/lapd/releases/tag/v0.1.2"&gt;https://github.com/megaproaktiv/lapd/releases/tag/v0.1.2&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>python</category>
    </item>
    <item>
      <title>GO-ing to production with Amazon (AWS) Bedrock RAG Part 1</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Sun, 10 Dec 2023 18:25:02 +0000</pubDate>
      <link>https://forem.com/aws-builders/go-ing-to-production-with-amazon-aws-bedrock-rag-part-1-16mf</link>
      <guid>https://forem.com/aws-builders/go-ing-to-production-with-amazon-aws-bedrock-rag-part-1-16mf</guid>
      <description>&lt;p&gt;The way from a cool POC (proof of concept), like a walk in &lt;a href="https://www.monets-garden.com/"&gt;monets garden&lt;/a&gt;, to a production-ready application for an RAG (Retrieval Augmented Generation) application with Amazon Bedrock and Amazon Kendra is paved with some work. Let`s get our hands dirty.&lt;/p&gt;

&lt;p&gt;With streamlit and langchain, you can quickly build a cool POC. This two-part blog is about what comes after that.&lt;/p&gt;

&lt;p&gt;See this AWS blog post &lt;a href="https://aws.amazon.com/blogs/machine-learning/quickly-build-high-accuracy-generative-ai-applications-on-enterprise-data-using-amazon-kendra-langchain-and-large-language-models/"&gt;here&lt;/a&gt; as an example of a POC. The &lt;a href="https://github.com/aws-samples/amazon-kendra-langchain-extensions/tree/main/kendra_retriever_samples"&gt;github repo&lt;/a&gt; uses streamlit and langchain to build a POC.&lt;/p&gt;

&lt;h2&gt;
  
  
  The story
&lt;/h2&gt;

&lt;p&gt;Let's assume you are an AWS architect in the “fancy GO knowledge company. You have shown the example above to the board, and they are excited.&lt;/p&gt;

&lt;p&gt;Hugo, is the CEO and says:&lt;/p&gt;

&lt;p&gt;"Well this is really cool stuff. We will really enhance the way we share knowledge in our company."&lt;/p&gt;

&lt;p&gt;You know what comes next: the BUT.&lt;/p&gt;

&lt;p&gt;"But, ... it will be used by many people, so it should &lt;em&gt;scale&lt;/em&gt; really well. Can you do that?&lt;/p&gt;

&lt;p&gt;You begin to remember a video you saw a while ago on youtube: &lt;a href="https://www.youtube.com/watch?v=BKorP55Aqvg"&gt;The expert&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;"And of cause, it should be &lt;em&gt;fast&lt;/em&gt;, &lt;em&gt;secure&lt;/em&gt; and &lt;em&gt;cost-effective&lt;/em&gt; at the same time"&lt;/p&gt;

&lt;p&gt;OK, you think 3 BUTs. I can handle that.&lt;/p&gt;

&lt;p&gt;"I also might have a few ideas about some more &lt;em&gt;features&lt;/em&gt;. To start with, give us the text snippets from the documents and page numbers also."&lt;/p&gt;

&lt;p&gt;You begin to sweat. You look to your boss Gordon (the CIO), for help. He may have slightly misinterpreted your look and added some more wishes: "And it should be &lt;em&gt;serverless&lt;/em&gt; and have &lt;em&gt;fast development cycles&lt;/em&gt;. And it should be &lt;em&gt;easy to maintain&lt;/em&gt;."&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to think
&lt;/h2&gt;

&lt;p&gt;You remember what the expert in the video said: "I can do anything, I can do absolutely anything." and start.&lt;/p&gt;

&lt;h2&gt;
  
  
  The POC architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DtEcsAH---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7uhb28aik2uk930inpd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DtEcsAH---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7uhb28aik2uk930inpd.png" alt="POC architecture" width="800" height="758"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new requirements are:&lt;/p&gt;

&lt;p&gt;0) Make the server available as a web app&lt;br&gt;
1) &lt;em&gt;Scale&lt;/em&gt; fast&lt;br&gt;
2) &lt;em&gt;Secure&lt;/em&gt;&lt;br&gt;
3) &lt;em&gt;cost-effective&lt;/em&gt;&lt;br&gt;
4) &lt;em&gt;features&lt;/em&gt; page numbers, excerpts&lt;br&gt;
5) &lt;em&gt;serverless&lt;/em&gt; (this is not really a requirement, but a solution architecture hint)&lt;br&gt;
6) &lt;em&gt;easy to maintain&lt;/em&gt;&lt;br&gt;
7) &lt;em&gt;fast development cycles&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution architecture
&lt;/h2&gt;
&lt;h3&gt;
  
  
  0) Make the server available as a web app
&lt;/h3&gt;

&lt;p&gt;You decide to deploy the streamlit server as a fargate container. You use &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/ecs_example.html"&gt;AWS CDK ApplicationLoadBalancedFargateService &lt;/a&gt; as a fast way to deploy a web server.&lt;/p&gt;

&lt;p&gt;Containerization of the application is relatively easy. Here is the dockerfile:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;FROM --platform=linux/amd64 python:3.11-slim&lt;/p&gt;

&lt;p&gt;WORKDIR /app&lt;/p&gt;

&lt;p&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \&lt;br&gt;
    build-essential \&lt;br&gt;
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;/p&gt;

&lt;p&gt;COPY streamlit/ streamlit/&lt;/p&gt;

&lt;p&gt;WORKDIR /app/streamlit&lt;br&gt;
RUN pip install --no-cache-dir -r requirements.txt&lt;/p&gt;

&lt;p&gt;EXPOSE 8501&lt;/p&gt;

&lt;p&gt;HEALTHCHECK CMD curl --fail &lt;a href="http://localhost:8501/_stcore/health"&gt;http://localhost:8501/_stcore/health&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ENTRYPOINT ["streamlit", "run", "app.py", "openai", "--server.port=8501", "--server.address=0.0.0.0"]&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;However, the monolithic architecture needs to scale better. So, you decide to split the application into two parts: the frontend and the backend.&lt;/p&gt;

&lt;p&gt;The frontend can run streamlit now, but it could be changed afterwards. To have a clean separation, the backend is an API Gateway with a Lambda function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ocrDFXDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hicg9mt1hcjh5nr1uyjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ocrDFXDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hicg9mt1hcjh5nr1uyjp.png" alt="backend" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1) &lt;em&gt;Scale&lt;/em&gt; fast
&lt;/h3&gt;

&lt;p&gt;With AWS Lambda now scaling &lt;a href="https://aws.amazon.com/blogs/aws/aws-lambda-functions-now-scale-12-times-faster-when-handling-high-volume-requests/"&gt;12 times faster&lt;/a&gt;, this part is covered. You are aware of the &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html"&gt;bedrock runtime scaling limits&lt;/a&gt; but you decide to deal with that later. Maybe with the throttling feature of API Gateway. You think of a SQS to decouple, but decide against &lt;em&gt;premature optimization&lt;/em&gt;, because it is the &lt;a href="https://stackify.com/premature-optimization-evil/"&gt;root of all evil&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2) &lt;em&gt;Secure&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;You know that with lambda you have checked many boxes of an &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/framework/the-review-process.html"&gt;AWS Well-Architected Framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A short commercial break:&lt;br&gt;
You decide to do an Well Architected Review with &lt;a href="https://www.tecracer.com/en/consulting/aws-well-architected-partner-program/"&gt;tecRacer&lt;/a&gt; later.&lt;/p&gt;

&lt;p&gt;The API gateway will run in a private subnet, and the lambda function will have no internet access. To secure development, you use API Keys.&lt;/p&gt;
&lt;h3&gt;
  
  
  3) &lt;em&gt;cost effective&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Lambda is a good choice to run unpredicted workloads. After reading a comparism about the storage requirements of Python vs GO in  the article &lt;a href="http://localhost:1313/2023/09/stop-llm/genai-hallucination-fast-serverless-kendra-rag-with-go.html"&gt;Stop LLM/GenAI hallucination fast: Serverless Kendra RAG with GO&lt;br&gt;
&lt;/a&gt;, you decide to try &lt;a href="https://www.go-on-aws.com/lambda-go/"&gt;GO&lt;/a&gt; as Lambda development language. This step will reduce storage and Lambda execution costs, especially cold starts.&lt;/p&gt;
&lt;h3&gt;
  
  
  4) &lt;em&gt;features&lt;/em&gt; page numbers, excerpts
&lt;/h3&gt;

&lt;p&gt;To analyse the POC, you look at the code of the langchain. In the langchain you look at the results given back from the Kendra query. They are created by &lt;code&gt;site-packages/langchain/retrievers/kendra.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;json&lt;br&gt;
{&lt;br&gt;
    "question": "my question",&lt;br&gt;
    "chat_history": [],&lt;br&gt;
    "answer": "some god answer",&lt;br&gt;
    "source_documents": [&lt;br&gt;
        {&lt;br&gt;
            "Document": "page_content= Document Title: ...  Document  Excerpt: ...",&lt;br&gt;
            "metadata": {&lt;br&gt;
                "source": "https: //s3.eu-west-1.amazonaws.com/a_bucket/a_document.pdf",&lt;br&gt;
                "title": "a title",&lt;br&gt;
                "excerpt": "...\n"&lt;br&gt;
            }&lt;br&gt;
        }&lt;br&gt;
    ]&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;No page numbers are returned. You decide not to change the langchain, but to code the calls directly. For bedrock, you find many examples in Python, Javascript and GO, e.g. &lt;a href="https://www.tecracer.com/blog/2023/10/climb-the-bedrock-with-python-javascript-and-go.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As you have to recode the logic anyway, you decide the fastest language, GO. See &lt;a href="https://www.tecracer.com/blog/2023/09/stop-llm/genai-hallucination-fast-serverless-kendra-rag-with-go.html"&gt;comparism&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  5) &lt;em&gt;serverless&lt;/em&gt; (this is not really a requirement, but a solution architecture hint)
&lt;/h3&gt;

&lt;p&gt;Yes.&lt;/p&gt;
&lt;h3&gt;
  
  
  6) &lt;em&gt;easy to maintain&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;The Lambda constructs implementations are good, but you decide to give AWS SAM a try. As it is focused on Serverless, it is less complex than AWS CDK. That means it is easier to learn and maintain.&lt;/p&gt;

&lt;p&gt;The monolithic architecture will be split into two parts in the solution architecture: frontend and  backend. This decoupled solution architecture is also easier to maintain.&lt;/p&gt;
&lt;h3&gt;
  
  
  7) &lt;em&gt;fast development cycles&lt;/em&gt;
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Frontend
&lt;/h4&gt;

&lt;p&gt;The deployment of the Streamlit server as fargate container with CDK takes a few minutes. As you can fully develop the streamlit application locally, this is not a problem. You develop and do integration tests locally.&lt;/p&gt;

&lt;p&gt;You can change the &lt;code&gt;app.py&lt;/code&gt; code and with a refresh of the browser see the changes.&lt;/p&gt;
&lt;h4&gt;
  
  
  Backend
&lt;/h4&gt;

&lt;p&gt;AWS Hero Yan Cui has pointed out in &lt;a href="https://www.youtube.com/watch?v=p3M1Y2Oi_Yk"&gt;"Test Honeycomb"&lt;/a&gt;, that with serverless environments, you should focus on integration tests. Although this is a ongoing discussion, you decide to focus on integration tests.&lt;/p&gt;

&lt;p&gt;That means you must deploy the lambda function to AWS to test it. Because you test hundreds of times a day, you need a fast deployment.&lt;/p&gt;

&lt;p&gt;The great thing is that you do not have to implement this yourself, as described in &lt;a href="https://www.tecracer.com/blog/2021/04/cdk-lambda-deployment-takes-about-a-minute-how-about-sub-second-function-code-deployment.html"&gt;sub seconds deployment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You give the new AWS SAM feature &lt;code&gt;sync&lt;/code&gt; a try. So you may do a full deployment with &lt;code&gt;sam deploy&lt;/code&gt; if resources change, or a fast `sync for code changes.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;sam sync --code --stack-name mystackname --watch&lt;/code&gt; code changes are deployed to AWS in a few seconds &lt;em&gt;in the background&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So in the time it takes you to switch the programm from &lt;a href="https://zed.dev/releases/stable/latest"&gt;editor&lt;/a&gt; to postman, the code is deployed. So the question now is: Is "no time" fast enough?&lt;/p&gt;

&lt;p&gt;On the terminal it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;task &lt;span class="nb"&gt;sync
&lt;/span&gt;task: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; sam &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--code&lt;/span&gt; &lt;span class="nt"&gt;--stack-name&lt;/span&gt; godemo &lt;span class="nt"&gt;--watch&lt;/span&gt;

The SAM CLI will use the AWS Lambda, Amazon API Gateway, and AWS StepFunctions APIs to upload your code without
performing a CloudFormation deployment. This will cause drift &lt;span class="k"&gt;in &lt;/span&gt;your CloudFormation stack.
&lt;span class="k"&gt;**&lt;/span&gt;The &lt;span class="nb"&gt;sync command &lt;/span&gt;should only be used against a development stack&lt;span class="k"&gt;**&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

You have enabled the &lt;span class="nt"&gt;--code&lt;/span&gt; flag, which limits sam &lt;span class="nb"&gt;sync &lt;/span&gt;updates to code changes only. To &lt;span class="k"&gt;do &lt;/span&gt;a &lt;span class="nb"&gt;complete &lt;/span&gt;infrastructure and code &lt;span class="nb"&gt;sync&lt;/span&gt;, remove the &lt;span class="nt"&gt;--code&lt;/span&gt; flag.
CodeTrigger not created as CodeUri or DefinitionUri is missing &lt;span class="k"&gt;for &lt;/span&gt;RagAPIGateway.
Sync watch started.
Syncing Lambda Function RagFunction...
Cache is invalid, running build and copying resources &lt;span class="k"&gt;for &lt;/span&gt;following functions &lt;span class="o"&gt;(&lt;/span&gt;RagFunction&lt;span class="o"&gt;)&lt;/span&gt;
Building codeuri: /Users/gglawe/letsbuild/git-megaproaktiv/go-rag-kendra-bedrock/lambda/query runtime: provided.al2 metadata: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'BuildMethod'&lt;/span&gt;: &lt;span class="s1"&gt;'makefile'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; architecture: arm64 functions: RagFunction
RagFunction: Running CustomMakeBuilder:CopySource
RagFunction: Running CustomMakeBuilder:MakeBuild
RagFunction: Current Artifacts Directory : /Users/gglawe/letsbuild/git-megaproaktiv/go-rag-kendra-bedrock/.aws-sam/auto-dependency-layer/RagFunction
&lt;span class="nb"&gt;env &lt;/span&gt;&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64 &lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 go build &lt;span class="nt"&gt;-tags&lt;/span&gt; lambda.norpc &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-s -w"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; bootstrap
&lt;span class="nb"&gt;cp&lt;/span&gt; ./bootstrap /Users/gglawe/letsbuild/git-megaproaktiv/go-rag-kendra-bedrock/.aws-sam/auto-dependency-layer/RagFunction/.
&lt;span class="nb"&gt;cp&lt;/span&gt; ./prompt.tmpl /Users/gglawe/letsbuild/git-megaproaktiv/go-rag-kendra-bedrock/.aws-sam/auto-dependency-layer/RagFunction/.
Missing physical resource. Infra &lt;span class="nb"&gt;sync &lt;/span&gt;will be started.
You have enabled the &lt;span class="nt"&gt;--code&lt;/span&gt; flag, which limits sam &lt;span class="nb"&gt;sync &lt;/span&gt;updates to code changes only. To &lt;span class="k"&gt;do &lt;/span&gt;a &lt;span class="nb"&gt;complete &lt;/span&gt;infrastructure and code &lt;span class="nb"&gt;sync&lt;/span&gt;, remove the &lt;span class="nt"&gt;--code&lt;/span&gt; flag.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SAM calles the &lt;code&gt;Makefile&lt;/code&gt; in the lambda directory. It takes less than a second to build the bootstrap file. You decide to go with this architecture concept. In Part 2 I will talk about how to implement it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You are happy with your architecture. You start to implement it. - This is the cliffhanger for part 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xcsdOp-k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dt26kkp7qq0ssh7cshj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xcsdOp-k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dt26kkp7qq0ssh7cshj2.png" alt="Solution" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Disclaimer
&lt;/h3&gt;

&lt;p&gt;The characters in this post are fictitious. Any resemblance to persons living or dead is purely coincidental.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I have learned
&lt;/h3&gt;

&lt;p&gt;In the last half year, my focus was on Generative AI and the RAG approach. It is a great way to enhance knowledge base access for larger companies or compleyx topics.&lt;/p&gt;

&lt;p&gt;This story shall entertain you and give you some ideas how to implement a RAG architecture and to think outside the "its all python/langchain" box. For your project it could also be JavaScript, but as you have seen, GO has some advantages too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second part
&lt;/h3&gt;

&lt;p&gt;The backend implementation will be covered in the second part of this blog post. All source code will be available on github and with some basic AWS knowledge you are ready to try it out yourself in minutes.&lt;/p&gt;

&lt;p&gt;Enjoy building!&lt;/p&gt;

&lt;p&gt;If you need developers and consulting for your next GenAI project, don't hesitate to contact the sponsor of this blog, &lt;a href="https://www.tecracer.com/kontakt/"&gt;tecRacer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more AWS development stuff, follow me on dev &lt;a href="https://dev.to/megaproaktiv"&gt;https://dev.to/megaproaktiv&lt;/a&gt;.&lt;br&gt;
Want to learn GO on AWS? &lt;a href="https://www.go-on-aws.com/"&gt;GO here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  See also
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/megaproaktiv/go-rag-kendra-bedrock"&gt;Full backend source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.anthropic.com/claude/docs/claude-on-amazon-bedrock"&gt;Claude on bedrock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/abhirockzz/amazon-bedrock-langchain-go"&gt;Amazon Bedrock implementations for the Langchain Go library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>aws</category>
      <category>genai</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Stop LLM/GenAI hallucination fast: Serverless Kendra RAG with GO</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Wed, 20 Sep 2023 16:56:58 +0000</pubDate>
      <link>https://forem.com/aws-builders/stop-llmgenai-hallucination-fast-serverless-kendra-rag-with-go-3gmc</link>
      <guid>https://forem.com/aws-builders/stop-llmgenai-hallucination-fast-serverless-kendra-rag-with-go-3gmc</guid>
      <description>&lt;p&gt;RAG is a way to approach the "hallucination" problem with LLM: A contextual reference increases the accuracy of the answers. Do you want to use RAG (Retrieval Augmented Generation) in production? The Python langchain library may be too slow for your production services. So what about serverless RAG in fast GO Lambda?&lt;/p&gt;

&lt;h2&gt;
  
  
  The AWS RAG sample solution
&lt;/h2&gt;

&lt;p&gt;In evaluating whether a technical solution is suitable, the focus is on the simplicity of development. So, the AWS sample uses the well-known  &lt;a href="https://docs.langchain.com/docs/"&gt;langchain&lt;/a&gt; library and a &lt;a href="https://streamlit.io/"&gt;streamlit&lt;/a&gt; server for the chat sample.&lt;/p&gt;

&lt;p&gt;Shifting to production ready solutions, the focus goes on speed and scalability.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://aws.amazon.com/blogs/machine-learning/quickly-build-high-accuracy-generative-ai-applications-on-enterprise-data-using-amazon-kendra-langchain-and-large-language-models/"&gt;Quickly build high-accuracy Generative AI applications on enterprise data using Amazon Kendra, LangChain, and large language models&lt;/a&gt; a StreamLit Python Server is used. See &lt;a href="https://github.com/aws-samples/amazon-kendra-langchain-extensions/tree/main/kendra_retriever_samples"&gt;kendra_retriever_samples&lt;/a&gt; for Code.&lt;/p&gt;

&lt;p&gt;As this is a good choice for working with low volume data science and LLM projects, a serverless solution does scale better.  in &lt;a href="https://aws.amazon.com/blogs/machine-learning/simplify-access-to-internal-information-using-retrieval-augmented-generation-and-langchain-agents/"&gt;Simplify access to internal information using Retrieval Augmented Generation and LangChain Agents&lt;/a&gt; a few examples are shown.&lt;/p&gt;

&lt;p&gt;And with a serverless solution we enter the polyglott world and can decide on the best language for the job. So what about GO?&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG with langchain, OpenAI and Amazon Kendra
&lt;/h2&gt;

&lt;p&gt;The sample server uses Python and the langchain library. However, despite its functionality, speed and memory consumption could be better.&lt;/p&gt;

&lt;p&gt;To check this assumption, I test the speed of the solution. I use just the langchain library in &lt;code&gt;kendra_retriever_open_ai.py&lt;/code&gt; from &lt;code&gt;amazon-kendra-langchain-extensions/kendra_retriever_samples&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XGW9__pV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u1i3wgcgrhwq7v6iqk23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XGW9__pV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u1i3wgcgrhwq7v6iqk23.png" alt="langchain" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure speed and size
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Python Speed
&lt;/h3&gt;

&lt;p&gt;The Kendra index from the AWS sample solution queries some AWS documentation. So a question should be about that topic.&lt;/p&gt;

&lt;p&gt;Therefore to measure &lt;strong&gt;speed&lt;/strong&gt; I use a single query "What is lex?" and perform it 10 times to reduce statistical randomness. This is only done locally, but should give a good measurement for lambda cold starts.&lt;/p&gt;

&lt;p&gt;The test calls &lt;code&gt;python3 kendra_chat_open_ai.py&lt;/code&gt; 10 times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./test.sh  35,19s user 7,26s system 57% cpu 1:13,63 total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So time measuring rod is 73 seconds per 10 iterations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python Size
&lt;/h3&gt;

&lt;p&gt;The app &lt;code&gt;kendra_retriever_open_ai.py&lt;/code&gt; itself is only a few K large.&lt;br&gt;
Most of the size goes to the pleora of libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;499M    .venv
108K    .venv/bin
490M    .venv/lib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So size measure is around 490 M.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Shifting to Lambda/GO
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dQYonWLw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ngdscu46ns3jjwnifcor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dQYonWLw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ngdscu46ns3jjwnifcor.png" alt="RAG serverless" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem with Python Lambda is the cold start, see &lt;a href="https://www.tecracer.com/blog/2023/07/custom-runtime-on-amazon-linux-2-go-outperforms-node-3x-and-python-2x-with-aws-lambda-cold-start-time.html"&gt;custom-runtime-on-amazon-linux-2-go-outperforms-node-3x-and-python-2x-with-aws-lambda-cold-start-time.html&lt;/a&gt;. Also what often is forgotten is the cost of updating the Lambda Python runtime over the years. With the GO backward compatibility promise you have less cost of code maintenance.&lt;/p&gt;

&lt;p&gt;See the comparism of cold starts Node vs Python vs GO custum runtime.&lt;br&gt;
 &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LfTKlYeU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.tecracer.com/blog/img/2023/07/custom-speed/coldstart-arm-2048.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LfTKlYeU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.tecracer.com/blog/img/2023/07/custom-speed/coldstart-arm-2048.png" alt="Custom runtime on Amazon Linux 2 - GO outperforms Node (3x) and Python (2x) with AWS Lambda Cold-start time" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation in Python
&lt;/h2&gt;

&lt;p&gt;To reproduce the behaviour of  &lt;code&gt;kendra_retriever_open_ai.py&lt;/code&gt; you have to understand the Langchain library. The general behaviour is:&lt;/p&gt;

&lt;p&gt;1) Retrieve Kendra data&lt;br&gt;
2) Generate Prompt with langchain "stuff" chain&lt;br&gt;
3) Call LLM for inference&lt;/p&gt;

&lt;p&gt;The steps in Python are:&lt;/p&gt;

&lt;p&gt;1) Retrieve Kendra data&lt;/p&gt;

&lt;p&gt;see &lt;code&gt;.venv/lib/python3.11/site-packages/langchain/retrievers/kendra.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;2) / 3) in &lt;code&gt;kendra_retriever_open_ai.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"gpt-3.5-turbo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;retriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AmazonKendraRetriever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;kendra_index_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The llm is initialized with the OpenAI model and the retriever with the Kendra index.&lt;/p&gt;

&lt;p&gt;The the chain is started&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;RetrievalQA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_chain_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;chain_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stuff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;retriever&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;chain_type_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chain_type_kwargs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;return_source_documents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the results are printed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resulting in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python kendra_chat_open_ai.py
Hello! How can I &lt;span class="nb"&gt;help &lt;/span&gt;you?
Ask a question, start a New search: or CTRL-D to exit.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; QueryText: what is lex?
 Amazon Lex is an AWS service &lt;span class="k"&gt;for &lt;/span&gt;building conversational interfaces &lt;span class="k"&gt;for &lt;/span&gt;applications using voice and text.
 With Amazon Lex, the same conversational engine that powers Amazon Alexa is now available to
 any developer, enabling them to build sophisticated, natural language chatbots into their new and existing applications.
 Amazon Lex provides the deep functionality and flexibility of natural language understanding &lt;span class="o"&gt;(&lt;/span&gt;NLU&lt;span class="o"&gt;)&lt;/span&gt;
 and automatic speech recognition &lt;span class="o"&gt;(&lt;/span&gt;ASR&lt;span class="o"&gt;)&lt;/span&gt; so developers can
 build highly engaging user experiences with lifelike, conversational interactions, and create new categories of products.
Sources:
https://docs.aws.amazon.com/lex/latest/dg/what-is.html
https://docs.aws.amazon.com/lex/latest/dg/security-iam.html
https://docs.aws.amazon.com/lex/latest/dg/security-iam.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing in GO
&lt;/h2&gt;

&lt;p&gt;1) Retrieve data from kendra&lt;/p&gt;

&lt;p&gt;I use the &lt;a href="https://docs.aws.amazon.com/sdk-for-go/api/service/kendra/#Kendra.RetrieveDocument"&gt;Amazon Kendra API&lt;/a&gt; directly:&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;parameters&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;kendra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RetrieveInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;IndexId&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;"1d936da5-4a20-462c-826f-bdddaa59e5e8"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;QueryText&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;"What is lex?"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// do retrieve&lt;/span&gt;
    &lt;span class="n"&gt;resp&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;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Stuff/Langchain&lt;/p&gt;

&lt;p&gt;In the end this should be a template solution, but to measure timing I just concat strings:&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;prompt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;pre&lt;/span&gt;
    &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResultItems&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" Document Title: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentTitle&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" Document Excerpt: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So some checks and using templates will add a few microseconds to the timing.&lt;/p&gt;

&lt;p&gt;3) OpenAI&lt;/p&gt;

&lt;p&gt;OpenAI just uses a REST api, for the request ther is a commnunity solution: &lt;code&gt;github.com/sashabaranov/go-openai&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that I call the &lt;a href="https://platform.openai.com/docs/api-reference/chat/object"&gt;Chat completion API call&lt;/a&gt; of openAI&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;resp&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;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateChatCompletion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletionRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GPT3Dot5Turbo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;MaxTokens&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Messages&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletionMessage&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatMessageRoleAssistant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&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;Temperature&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&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;h3&gt;
  
  
  GO Size
&lt;/h3&gt;

&lt;p&gt;With GO you get a compile binary with all libraries included:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lkh&lt;/span&gt; dist
total 9728
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt;@ 1 gernotglawe  staff   9,5M 15 Sep 18:03 rag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So overall size is 9,5 M.&lt;br&gt;
Compared to 490M, this is about 50 times smaller.&lt;/p&gt;

&lt;p&gt;With ARM bases Lambda it is even smaller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lkh&lt;/span&gt; dist
total 9348
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt;@ 1 gglawe  staff   6,6M 20 Sep 18:34 bootstrap
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@ 1 gglawe  staff   2,6M 20 Sep 18:34 bootstrap.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GO Speed
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HYrmWUkI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ketlmyfnf6rs8cwhkrdb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HYrmWUkI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ketlmyfnf6rs8cwhkrdb.png" alt="GO speed" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GO Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Amazon Lex is an AWS service &lt;span class="k"&gt;for &lt;/span&gt;building conversational interfaces &lt;span class="k"&gt;for &lt;/span&gt;applications using voice and text.
It allows developers to build sophisticated, natural language chatbots into their new and existing applications,
using the same conversational engine that powers Amazon Alexa. With Amazon Lex, developers can create highly
engaging user experiences with lifelike, conversational interactions and
take advantage of natural language understanding &lt;span class="o"&gt;(&lt;/span&gt;NLU&lt;span class="o"&gt;)&lt;/span&gt; and automatic speech recognition &lt;span class="o"&gt;(&lt;/span&gt;ASR&lt;span class="o"&gt;)&lt;/span&gt; capabilities.
Additionally, Amazon Lex enables developers to quickly build conversational chatbots.
Sources:
https://docs.aws.amazon.com/lex/latest/dg/what-is.html
https://docs.aws.amazon.com/lex/latest/dg/security-iam.html
https://docs.aws.amazon.com/lex/latest/dg/security-iam.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And 10 iterations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./test.sh  0,11s user 0,13s system 0% cpu 46,088 total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;73 seconds/46 seconds = 1,58&lt;/p&gt;

&lt;p&gt;So speed is about 46 seconds, which is about 50% faster. The 8% is rounded because there is still some checking etc to be done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to Lambda
&lt;/h2&gt;

&lt;p&gt;A few considerations:&lt;/p&gt;

&lt;h3&gt;
  
  
  Language
&lt;/h3&gt;

&lt;p&gt;Note the default language of the kendra data source. You need to set the language of the query to the same language.&lt;br&gt;
For German you need to:&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;parameters&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;kendra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RetrieveInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;IndexId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;QueryText&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AttributeFilter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AttributeFilter&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;AndAllFilters&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AttributeFilter&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;EqualsTo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentAttribute&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;Key&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;"_language_code"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentAttributeValue&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="s"&gt;"de"&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="p"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Python it would look like :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;attribute_filter_json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'''
  { "AndAllFilters": [
              {
                  "EqualsTo": {
                      "Key": "_language_code",
                      "Value": {
                          "StringValue": "de"
                      }
                  }
              }
          ]
  }
  '''&lt;/span&gt;

&lt;span class="n"&gt;attribute_filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute_filter_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;retriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AmazonKendraRetriever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;kendra_index_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attribute_filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attribute_filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Timeout
&lt;/h3&gt;

&lt;p&gt;The default timeout of lambda is 3 seconds. As the results from the LLM api can take some time, you need to increase the timeout to at least a minute.&lt;/p&gt;

&lt;p&gt;A typical log entry looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2023/09/20 16:34:53 INFO Lambda start
2023/09/20 16:34:53 INFO Kendra start
2023/09/20 16:34:54 INFO Kendra end
2023/09/20 16:34:54 INFO OpenAI start
2023/09/20 16:35:15 INFO OpenAI end
...LLM output...
2023/09/20 16:35:15 INFO Lambda end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AWS Managed Role
&lt;/h3&gt;

&lt;p&gt;As the Kendra retrieve API is quite new, the AWS managed role does not have the permission to call it.&lt;br&gt;
You need to add the permission to the role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Langchain great for testing several choices. When you have found your model and type of langchain, you can optimize for size and speed. With GO you can get 50 times less size and 50% faster speed.&lt;/p&gt;

&lt;p&gt;If you need consulting for your serverless project, don't hesitate to get in touch with the sponsor of this blog, &lt;a href="https://www.tecracer.com/kontakt/"&gt;tecRacer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more AWS development stuff, follow me on dev &lt;a href="https://dev.to/megaproaktiv"&gt;https://dev.to/megaproaktiv&lt;/a&gt;.&lt;br&gt;
Want to learn GO on AWS? &lt;a href="https://www.go-on-aws.com/"&gt;GO here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llm</category>
      <category>serverless</category>
      <category>go</category>
      <category>genai</category>
    </item>
    <item>
      <title>AWS Lambda recursive loop detection - but not (yet) for S3</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Mon, 17 Jul 2023 16:05:44 +0000</pubDate>
      <link>https://forem.com/aws-builders/lambda-recursive-loop-detection-but-not-yet-for-s3-1d7a</link>
      <guid>https://forem.com/aws-builders/lambda-recursive-loop-detection-but-not-yet-for-s3-1d7a</guid>
      <description>&lt;p&gt;Recursive loops in AWS Lambda are the root of all evil because you create a large bill quickly. &lt;/p&gt;

&lt;p&gt;You might think the danger is over because of &lt;a href="https://aws.amazon.com/blogs/compute/detecting-and-stopping-recursive-loops-in-aws-lambda-functions-2/"&gt;new AWS recursive loop detection&lt;/a&gt;. But what about S3 buckets and Lambda? It's time for a closer look:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A word of caution&lt;/strong&gt;: Be very careful when you test this. You could create large AWS bills.&lt;/p&gt;

&lt;p&gt;TL;DR: Never ever use the same bucket for invoking a Lambda and write the output.&lt;/p&gt;

&lt;p&gt;Some testing infrastructure:&lt;/p&gt;

&lt;p&gt;1) A S3 bucket (CDK, GO)&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;bucky&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;awss3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&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;"incoming-ring"&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;awss3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BucketProps&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;BlockPublicAccess&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;awss3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BlockPublicAccess_BLOCK_ALL&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;awscdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RemovalPolicy_DESTROY&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;2) A Lambda Function Resource&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;ringFunction&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;awslambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&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;"ring"&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;awslambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FunctionProps&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Description&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;"ring - test recursive loop stop"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;FunctionName&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;"ring"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;LogRetention&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;           &lt;span class="n"&gt;awslogs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RetentionDays_THREE_MONTHS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;MemorySize&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;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                &lt;span class="n"&gt;awscdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration_Seconds&lt;/span&gt;&lt;span class="p"&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;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                   &lt;span class="n"&gt;awslambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code_FromAsset&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;lambdaPath&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;awss3assets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AssetOptions&lt;/span&gt;&lt;span class="p"&gt;{}),&lt;/span&gt;
            &lt;span class="n"&gt;Handler&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;"main"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                &lt;span class="n"&gt;awslambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime_GO_1_X&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;DeadLetterQueueEnabled&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;Bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;DeadLetterQueue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="n"&gt;dlq&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;3) A Lambda Function, which gets the PutObject Event and write &lt;em&gt;to the same bucket&lt;/em&gt;:&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;someString&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"hello world&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;and hello go and more"&lt;/span&gt;
    &lt;span class="n"&gt;myReader&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;someString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;resp&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;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TODO&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;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObjectInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Bucket&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;bucket&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Key&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;s3input&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;myReader&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;4) An event, which triggers the Lambda function each time an object is Put to the bucket:&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;myHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddEventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewS3EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucky&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;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S3EventSourceProps&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;awss3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventType&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;awss3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventType_OBJECT_CREATED&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;Now we have:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n0bzHYZ0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjrjwuf0jyz5fe3ontyj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n0bzHYZ0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjrjwuf0jyz5fe3ontyj.png" alt="Ring architecture" width="204" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And because I am very careful, I decide to create a recursive loop stopper inside the Lambda function:&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;GlobalCounter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;GlobalCounter&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Counter exceeded&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&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;And in the Console, we set the concurrency to 1:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yJPE5cNL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4akfsj8lxvzc5yppl3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yJPE5cNL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4akfsj8lxvzc5yppl3t.png" alt="concurrency" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we test the recursive loop, we &lt;em&gt;always&lt;/em&gt; have a browser window with the concurrency open. Because: Setting the concurrency to &lt;strong&gt;0&lt;/strong&gt; is the fastest way to stop an endless loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test
&lt;/h2&gt;

&lt;p&gt;Lets test whether S3-Lambda-S3 recursive loops are still possible:&lt;/p&gt;

&lt;p&gt;We use &lt;a href="https://github.com/TylerBrock/saw"&gt;saw&lt;/a&gt; to tail lambda log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;saw watch /aws/lambda/ring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then start the recursive loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;README.md s3://ring-incomingring3eb9a9b9-1a8q8cp8qm2ch/readme1.md
upload: ./README.md to s3://ring-incomingring3eb9a9b9-1a8q8cp8qm2ch/readme1.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Very fast you get the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[2023-07-17T17:34:39+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) INIT_START Runtime Version: go:1.v18 Runtime Version ARN: arn:aws:lambda:eu-central-1::runtime:ccb68acb59818f9df9b10924cc6c83ca6eaf4067f70ba861c0e211b59e8af729
[2023-07-17T17:34:39+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) START RequestId: 10fba020-0e93-4430-a9d7-728d34b146c9 Version: $LATEST
[2023-07-17T17:34:39+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) Counter: 1
[2023-07-17T17:34:39+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) Etag:  446cce24a7c945503232494e881150ec
[2023-07-17T17:34:39+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) Seq:  0064B55F8E25E34383
[2023-07-17T17:34:39+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) END RequestId: 10fba020-0e93-4430-a9d7-728d34b146c9
[2023-07-17T17:34:39+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) REPORT RequestId: 10fba020-0e93-4430-a9d7-728d34b146c9   Duration: 92.67 ms  Billed Duration: 93 ms  Memory Size: 1024 MB    Max Memory Used: 39 MB  Init Duration: 110.22 ms
[2023-07-17T17:34:40+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) START RequestId: 97cf261e-b272-437f-9637-b7573cfb0fa8 Version: $LATEST
[2023-07-17T17:34:40+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) Counter: 2
[2023-07-17T17:34:40+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) Etag:  a9c4c5f405a3e4cc6ba3b6b355da0e71
[2023-07-17T17:34:40+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) Seq:  0064B55F8FD24FBE14
[2023-07-17T17:34:41+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) END RequestId: 97cf261e-b272-437f-9637-b7573cfb0fa8
[2023-07-17T17:34:41+02:00] (2023/07/17/[$LATEST]e27ec4706f1a4fb0a6d3ca1fdb23bbc0) REPORT RequestId: 97cf261e-b272-437f-9637-b7573cfb0fa8   Duration: 27.20 ms  Billed Duration: 28 ms  Memory Size: 1024 MB    Max Memory Used: 40 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Counter: 1
Counter: 2
...
Counter: 19
Counter: 20
Counter exceeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And because &lt;a href="https://aws.amazon.com/blogs/compute/detecting-and-stopping-recursive-loops-in-aws-lambda-functions-2/"&gt;AWS BLOG about recursive loops&lt;/a&gt; says:&lt;br&gt;
"Lambda now detects the function running in a recursive loop between supported services after exceeding 16 invocations. It returns a RecursiveInvocationException to the caller."&lt;/p&gt;

&lt;p&gt;We are now sure that the loop detection for S3 is &lt;em&gt;not&lt;/em&gt; working because we counted to 20!&lt;/p&gt;

&lt;p&gt;And although I implemented a loop stopper  very fast, I started a recursive loop:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E5rwgT1S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n9vsri4nlelm6glgb8pz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E5rwgT1S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n9vsri4nlelm6glgb8pz.png" alt="Loop" width="442" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now I set the concurrency to 0 again and delete the Lambda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;It's proven:&lt;/p&gt;

&lt;p&gt;Recursive Loop detection does &lt;strong&gt;not&lt;/strong&gt; stop S3 recursive loops, so do use &lt;em&gt;different buckets&lt;/em&gt; for event trigger and Lambda output!&lt;/p&gt;

&lt;p&gt;So be careful out there.&lt;br&gt;
And checkout my website &lt;a href="https://www.go-on-aws.com/"&gt;go-on-aws&lt;/a&gt; or - even better :) - use my new udemy course &lt;a href="https://www.udemy.com/course/go-on-aws-coding-serverless-and-iac/?referralCode=954E43527F32E22BB1C7"&gt;GO on AWS - Coding, Serverless and Infrastructure as Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enjoy building!&lt;br&gt;
Gernot&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A new simple approach to diagram as code on AWS with CDK and D2</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Sun, 02 Apr 2023 16:54:09 +0000</pubDate>
      <link>https://forem.com/aws-builders/a-new-simple-approach-to-diagram-as-code-on-aws-with-cdk-and-d2-17ef</link>
      <guid>https://forem.com/aws-builders/a-new-simple-approach-to-diagram-as-code-on-aws-with-cdk-and-d2-17ef</guid>
      <description>&lt;p&gt;A diagram should convey a clear message about the intention of the architecture. For this message, you only need a few primary resources. Most generated diagrams are overloaded. This new app generates a diagram as code from your annotations in the CDK code.&lt;/p&gt;

&lt;p&gt;When I am doing AWS Training, participants often state the wish that diagrams are automatically drawn from AWS resources.&lt;br&gt;
As this is possible with solutions like cfn-diag, the generated diagrams usually need more information to be helpful.&lt;br&gt;
A diagram should convey a clear message about the architecture's intention. For this message, you only need a few primary resources. The other "glue" resources are necessary for the proper functions but not to show the logical view of a diagram.&lt;/p&gt;

&lt;p&gt;The traditional approaches to this problem are:&lt;/p&gt;

&lt;p&gt;1) Only use it if you have a small number of resources, like the CloudFormation drawing tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SPuu8FS2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n052co3shysuzm2wf1dc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SPuu8FS2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n052co3shysuzm2wf1dc.png" alt="Cloudformation" width="487" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2) Filter resources from the diagram afterwards, like &lt;code&gt;cfn-dia&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3TJMpj2W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m3l20vuqdp1sabaa32ub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3TJMpj2W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m3l20vuqdp1sabaa32ub.png" alt="cfn-diag" width="478" height="562"&gt;&lt;/a&gt;&lt;br&gt;
3) Draw manually&lt;/p&gt;

&lt;p&gt;All solutions have a low degree of automation and do not synchronize with changes in the infrastructure.&lt;/p&gt;

&lt;p&gt;A helpful solution should:&lt;/p&gt;

&lt;p&gt;1) be fully automated, with no manual steps&lt;br&gt;
2) Be 100% synched with deployed resources. Show &lt;em&gt;what is&lt;/em&gt; and not what should be&lt;br&gt;
3) Take meaningful names from the &lt;em&gt;Code&lt;/em&gt; part of Infrastructure as Code. The often used CloudFormation logical ID is not helpful.&lt;/p&gt;

&lt;p&gt;And my main requirement:&lt;/p&gt;

&lt;p&gt;4) Show only those resources which I think are needed, to show the main purpose of the architecture&lt;/p&gt;
&lt;h2&gt;
  
  
  A simple approach to DaC
&lt;/h2&gt;

&lt;p&gt;1) Generate &lt;a href="https://d2lang.com/tour/intro/"&gt;D2&lt;/a&gt; code from CDK code. D2 will take care of rendering.&lt;/p&gt;

&lt;p&gt;2) Poll CloudFormation for the deployment status of the resource. Only resources which are beeing created or are created are shown.&lt;/p&gt;

&lt;p&gt;3) Use the CDK Construct ID as title for a resources, not the logical ID or the physical ID.&lt;/p&gt;

&lt;p&gt;4) Add very few information to the CDK code, to keep thinks simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Show&lt;/code&gt; - the Construct is shown on the diagram&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Connection&lt;/code&gt; - a connection is drawn from source Construct to target Construct&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Contains&lt;/code&gt; - Resources like a VPC, which contain other resources&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Example with a serverless architecture
&lt;/h2&gt;

&lt;p&gt;I take &lt;a href="https://github.com/megaproaktiv/adot-xraystarter"&gt;simple serverless architecture&lt;/a&gt; as a starting point.&lt;/p&gt;

&lt;p&gt;These are the lines added for the diagramm information into the &lt;a href="https://github.com/megaproaktiv/adot-xraystarter/blob/main/lib/xraystarter-stack.ts"&gt;TypeScript file&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;d2&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../lib/d2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fnTS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fnPy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fnGO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucky&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucky&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fnTS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fnGO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fnPy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fnTS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fnGO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fnPy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a S3 Bucket named "bucky", some Lambda FNunctions, a SNS topic and a DynamoDB table.&lt;/p&gt;

&lt;p&gt;And I copy  a small TypeScript &lt;a href="https://github.com/megaproaktiv/cdk2d2/blob/main/testdata/lib-ts/d2.ts"&gt;file&lt;/a&gt; into the &lt;code&gt;lib&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;I deploy the infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I generate a d2 diagramm description with &lt;a href="https://github.com/megaproaktiv/cdk2d2"&gt;cdk2d2&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; cdk2d2 generate adotstarter-auto &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the first parameter is the &lt;em&gt;name&lt;/em&gt; of the stack  and the second parameter is the &lt;em&gt;directory&lt;/em&gt; of the CDK app. This creates a file &lt;code&gt;adotstarter-auto.d2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With d2 I generate a png of this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;d2 adotstarter-auto.d2 adotstarter-auto.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we got:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WdstF1s1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sz22v37x3v8b8nlrujz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WdstF1s1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sz22v37x3v8b8nlrujz6.png" alt="cdn" width="880" height="887"&gt;&lt;/a&gt;&lt;br&gt;
instead of the quite confusing CloudFormation diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jVcA7lam--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ssrouy7usavz9gf2cpe1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jVcA7lam--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ssrouy7usavz9gf2cpe1.png" alt="d2" width="654" height="515"&gt;&lt;/a&gt;&lt;br&gt;
You see on first sight what the architecure is doing &lt;em&gt;and&lt;/em&gt; the name of the Functions are not &lt;code&gt;adotstartergo2987B222&lt;/code&gt;, which would be the CloudFormation &lt;em&gt;logical&lt;/em&gt; ID, but the &lt;em&gt;Construct&lt;/em&gt; ID from the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fnGO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Function&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;adotstarter-go&lt;/span&gt;&lt;span class="dl"&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;h2&gt;
  
  
  Inside cdk2d2
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DV7C-LFh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88d5qbwckde5er4x9jj4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DV7C-LFh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88d5qbwckde5er4x9jj4.png" alt="dataflow" width="880" height="389"&gt;&lt;/a&gt;&lt;br&gt;
With the information from the CDK manifest file from the &lt;a href="https://github.com/aws/aws-cdk/tree/v1-main/packages/%40aws-cdk/cloud-assembly-schema"&gt;cloud-assembly&lt;/a&gt;, q construct like the lambda function "fnGO" is generated as D2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adotstartergo2987B222: adotstarter-go{
 icon: https://icons.terrastruct.com/aws%2FCompute%2FAWS-Lambda_Lambda-Function_light-bg.svg
 style.fill:"lightgreen"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the id of the artefact is the cfn logicalid and the title is the CDK construct ID.&lt;br&gt;
The icon is set according to the CloudFormation Resource type. See &lt;a href="https://github.com/megaproaktiv/cdk2d2/blob/main/monitor/icon.go"&gt;icon.go&lt;/a&gt; for mappings. The color &lt;code&gt;style.fill&lt;/code&gt; is set according to the deployment state of the resource.&lt;/p&gt;

&lt;p&gt;So when you change the name of the construct, the diagram is updated with the new name.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You&lt;/em&gt; define the resources which should be shown on the diagramm in your code, because only you know which resources are the main resources.&lt;br&gt;
For example the helper lambda which CDK uses to set the retention time on the CloudWatch logs should usually now be shown.&lt;/p&gt;
&lt;h2&gt;
  
  
  Now to the synchronisation part
&lt;/h2&gt;

&lt;p&gt;I destroy the stack. Now we use three terminal windows:&lt;/p&gt;

&lt;p&gt;1) cdk2d2 Script generator&lt;br&gt;
2) d2 rendering&lt;br&gt;
3) CDK deploy.&lt;/p&gt;

&lt;p&gt;All running in the same CDK app base directory.&lt;/p&gt;
&lt;h3&gt;
  
  
  Terminal 1
&lt;/h3&gt;

&lt;p&gt;On terminal 1 we start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk2d2 watch adotstarter-auto &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will update the stackname.d2 each 4 seconds with then current deployment state of the construct resource.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminal 2
&lt;/h3&gt;

&lt;p&gt;On this terminal we start the rendering process with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;d2 &lt;span class="nt"&gt;--watch&lt;/span&gt; adotstarter-auto.d2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open a browser windows, where d2 renders the live state of the stack.de diagram.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminal 3
&lt;/h3&gt;

&lt;p&gt;Here we start the CDK deployment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  First picture
&lt;/h3&gt;

&lt;p&gt;Will show nothing, because the stack does not exists.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second picture
&lt;/h3&gt;

&lt;p&gt;Will show the stack in the upper left with the total number of resources and the deployment percentage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wZZ4q15n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/204q6090iuvn6orik1kk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wZZ4q15n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/204q6090iuvn6orik1kk.png" alt="Stack only" width="180" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Life state change
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pqys24eK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15u7ibjhkxvn5mn8rrbm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pqys24eK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15u7ibjhkxvn5mn8rrbm.png" alt="Life 1" width="260" height="260"&gt;&lt;/a&gt;&lt;br&gt;
Resources which are beeing created are colored yellow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C2V2_Pja--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hpfl0zkga3esai75bhc1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C2V2_Pja--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hpfl0zkga3esai75bhc1.png" alt="Life 2" width="264" height="252"&gt;&lt;/a&gt;&lt;br&gt;
Resources which are created are colored green.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fully deployed state
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XvZmcjZG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ch8ar5aiwx1mx9fssikw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XvZmcjZG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ch8ar5aiwx1mx9fssikw.png" alt="Life 3" width="627" height="642"&gt;&lt;/a&gt;&lt;br&gt;
100% resources created.&lt;/p&gt;

&lt;p&gt;For each resource you will see the deployment status as color.&lt;br&gt;
This way you can watch CloudFormation deploying your resources. The order of the deployment will surprise you sometimes...&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies
&lt;/h2&gt;

&lt;p&gt;The metainformation are stored as metadata:&lt;/p&gt;

&lt;h3&gt;
  
  
  Show
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;resource.node.addMetadata("Show", "true")&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;resource.node.addMetadata("Connection", otherResource.node.id)&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Container
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;resource.node.addMetadata("Container", otherResource.node.id)&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Container example
&lt;/h2&gt;

&lt;p&gt;Of course we are talking about a graphical container!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pygDnPzz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5fz3tmrm72jkuscaa79d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pygDnPzz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5fz3tmrm72jkuscaa79d.png" alt="VPC" width="320" height="574"&gt;&lt;/a&gt;&lt;br&gt;
For a VPC you add a "Container" relationship. Only one level is supported, so vpc-subnet will not work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now try it out for yourself
&lt;/h2&gt;

&lt;p&gt;Please send suggestions, errors, images for the gallery etc. Add prs for new features to &lt;a href="https://github.com/megaproaktiv/cdk2d2"&gt;cdk2d2&lt;/a&gt; issues. There are also some more examples. Releases can be found &lt;a href="https://github.com/megaproaktiv/cdk2d2/releases"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like it, please star the repo!&lt;/p&gt;

&lt;p&gt;Happy building!&lt;/p&gt;

&lt;p&gt;If you need consulting for your serverless project, don't hesitate to get in touch with the sponsor of this blog, &lt;a href="https://www.tecracer.com/kontakt/"&gt;tecRacer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more AWS development stuff, follow me on dev &lt;a href="https://dev.to/megaproaktiv"&gt;https://dev.to/megaproaktiv&lt;/a&gt;.&lt;br&gt;
Want to learn GO on AWS? &lt;a href="https://www.go-on-aws.com/"&gt;GO here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  See also
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/megaproaktiv/cdk2d2"&gt;cdk2d2 Source code &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://d2lang.com/tour/intro/"&gt;d2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/cdk/"&gt;AWS CDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cdk</category>
      <category>d2</category>
      <category>documentation</category>
    </item>
    <item>
      <title>Check external links in PowerPoint automatically from the command line</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Thu, 23 Mar 2023 15:04:34 +0000</pubDate>
      <link>https://forem.com/megaproaktiv/check-external-links-in-powerpoint-1ja</link>
      <guid>https://forem.com/megaproaktiv/check-external-links-in-powerpoint-1ja</guid>
      <description>&lt;p&gt;How do I check whether all my external HTTPs links in the PowerPoints slides are OK? I asked chatgp.&lt;/p&gt;

&lt;p&gt;The answer was not right, but it included the idea of just parsing the XML from powerpoint.&lt;/p&gt;

&lt;p&gt;So it boiled down to simple steps:&lt;/p&gt;

&lt;p&gt;1) Looking in directory ppt/slides/_rels/slide&lt;br&gt;
2) Finding links with xmlquery&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;elements := xmlquery.Find(doc, "//Relationship")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Do a&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    response, errors := http.Get(web)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And check it all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; go run main.go check testdata/someuris.pptx
❌ Target https://www.john-doe.com/ is not reachable in slide: slide3
☑️ Target https://www.google.de/ is reachable in slide: slide3
❌ Target https://www.john-doe.com/ is not reachable in slide: slide2
☑️ Target https://www.google.de/ is reachable in slide: slide2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the app: &lt;a href="https://github.com/megaproaktiv/linkchecker"&gt;https://github.com/megaproaktiv/linkchecker&lt;/a&gt;&lt;br&gt;
With binaries released: &lt;a href="https://github.com/megaproaktiv/linkchecker/releases/tag/v0.1.4"&gt;https://github.com/megaproaktiv/linkchecker/releases/tag/v0.1.4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simple as that.&lt;br&gt;
Enjoy!&lt;/p&gt;

&lt;p&gt;For more good GO and AWS stuff:&lt;br&gt;
&lt;a href="https://www.go-on-aws.com/"&gt;https://www.go-on-aws.com/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Check S3 storage classes from command line</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Wed, 15 Mar 2023 17:50:07 +0000</pubDate>
      <link>https://forem.com/aws-builders/check-s3-storage-classes-from-command-line-31c3</link>
      <guid>https://forem.com/aws-builders/check-s3-storage-classes-from-command-line-31c3</guid>
      <description>&lt;p&gt;... it's not so easy, so I created a small GO program:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/megaproaktiv/storageclass"&gt;https://github.com/megaproaktiv/storageclass&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scanned 1.8 Million files in 5 minutes.&lt;/p&gt;

&lt;p&gt;It will show you the storage classes of all buckets like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bucket: archivemac123
STANDARD 3747
STANDARD_IA 183
8.6 GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enjoy!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Switch &amp; Leapp-cli - AWS session management 100% command line</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Sun, 05 Mar 2023 13:33:59 +0000</pubDate>
      <link>https://forem.com/aws-builders/switch-leapp-cli-aws-session-management-100-command-line-23f</link>
      <guid>https://forem.com/aws-builders/switch-leapp-cli-aws-session-management-100-command-line-23f</guid>
      <description>&lt;p&gt;According to the well architected framework you should not store keys as cleartext. So why do you store your AWS credentials in a credential file as clear unencrypted text? The answer is: Because it is convenient! I show you a way to handle your static or SSS AWS credentials simple &lt;em&gt;and&lt;/em&gt; secure.&lt;/p&gt;

&lt;p&gt;You need three little tools:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.leapp.cloud/index.html" rel="noopener noreferrer"&gt;Leap&lt;/a&gt;, the new &lt;a href="https://www.leapp.cloud/cli" rel="noopener noreferrer"&gt;leapp-cli&lt;/a&gt; and &lt;a href="https://github.com/megaproaktiv/switchaws" rel="noopener noreferrer"&gt;switchaws&lt;/a&gt;.  You will get a zero byte credentials file, temporal credentials and command line handling with fast &amp;amp; easy installable tools&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start for the impatient
&lt;/h2&gt;

&lt;p&gt;Assuming you have an AWS SSO login and a profile called &lt;code&gt;letsbuild&lt;/code&gt;. After installing the tools you can start the session with these two commands:&lt;/p&gt;

&lt;p&gt;One&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;leapp session start letsbuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch letsbuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Before
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -l ~/.aws/credentials
-rw-------@ 1 jdoe  staff  0  3 Mär 12:45 /Users/jdoe/.aws/credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -l ~/.aws/credentials
-rw-------@ 1 jdoe  staff  831  3 Mär 12:45 /Users/jdoe/.aws/credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and also filled environment variables like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS_DEFAULT_REGION=eu-central-1
AWS_DEFAULT_PROFILE=letsbuild
AWS_REGION=eu-central-1
AWS_ACCESS_KEY_ID=ASIA3SHER36FBEBMXR22
AWS_SECRET_ACCESS_KEY=P9kWKJKgsOWBMOAW7a5aRI7apt31CXAuXpfNsoeC
AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjECwaCWV1LXdlc3QtMSJGMEQCIE0KfNquOOCxf9UuXxgnWnvCeK6JeYWnqXmmz48fnzP+AiAwWRh7qnXXR8FkEfpkc5...9UmXa9PxI4Qj0ObcxLP8/YQBbIkCgs0+C7xWj/e1lmKhSLlhjRI04Mlj1Y9EomihaH/YEGEAXJ1sySpcgZJAHW6n02E7LvUAhV9ODYX66AFbRdqRrFZXIlDN5J0MalU18gNts3d1OA==
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you can start using the profile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws sts get-caller-identity
{
    "UserId": "AIDAAAABBBBCCCAW",
    "Account": "777555666888",
    "Arn": "arn:aws:iam::777555666888:user/jdoe"
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative approaches
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using profiles only with leap
&lt;/h3&gt;

&lt;p&gt;1) start session &lt;br&gt;
&lt;code&gt;leapp session start letsbuild&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;2) use profiles with each call:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aws sts get-caller-identity --profile letsbuild&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure leaps for default profile
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fo06f5wypzfyinkezzbvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fo06f5wypzfyinkezzbvw.png" alt="Default profile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The downside:  The aws cli first looks for credentials in the environment variables. If it finds &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; &amp;amp; co , the profile in the credentials file will not be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you get out of this approach?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Secure storage of credentials keys
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Not stored as  clear text as file, but in the MAC key chain&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Secure usage of temporal credentials
&lt;/h3&gt;

&lt;p&gt;With AWS SSO you always get temporal credentials. With a static IAM user access key, you would use static credentials. leapp uses these static keys to generate temp credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy installation and long term stability
&lt;/h3&gt;

&lt;p&gt;I have used &lt;a href="https://github.com/trek10inc/awsume" rel="noopener noreferrer"&gt;&lt;code&gt;awsume&lt;/code&gt;&lt;/a&gt; a long time. Then I got a new Macbook and lost 1/2 hour installing different python versions. So I programmed &lt;code&gt;switchaws&lt;/code&gt; in go to get a single executable. And, yes: I declare guilty of the "not invented here" syndrom :) .&lt;/p&gt;

&lt;p&gt;Installation is straightforward:&lt;/p&gt;

&lt;p&gt; 1) copy the matching binary link in a directory which is in your &lt;code&gt;$PATH &lt;/code&gt;&lt;br&gt;
2) copy the wrapper tile also in that directory  &lt;br&gt;
3) Set an alias&lt;/p&gt;

&lt;p&gt; and you are done!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Leapp works great with either static ACCESS_KEY or sso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@isaactanlishung?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Isaac Li Shung Tan&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/switch?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>leapp</category>
      <category>switchaws</category>
    </item>
    <item>
      <title>Building an AWS Lambda Telemetry API extension for direct logging to Grafana Loki</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Fri, 17 Feb 2023 09:04:47 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-an-aws-lambda-telemetry-api-extension-for-direct-logging-to-grafana-loki-de4</link>
      <guid>https://forem.com/aws-builders/building-an-aws-lambda-telemetry-api-extension-for-direct-logging-to-grafana-loki-de4</guid>
      <description>&lt;p&gt;In hybrid architectures, serverless functions work together with container solutions. Lambda logs have to be translated when you don`t choose CloudWatch Logs. The old way of doing this is through subscription filters using additional Lambda functions for log transformation. With the Lambda Telemetry API there is a more elegant, performant and cost-effective way. I am using Grafana Loki as a working example and show you how to build a working Lambda-Loki Telemetry APi extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a centralized Logging solution in hybrid environments
&lt;/h2&gt;

&lt;p&gt;Using e.g. Grafana Loki as your centralized logging solution, where container &lt;em&gt;and&lt;/em&gt; Lambda Functions should ship there logs is quite common. &lt;/p&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%2Fvwk2pybkfwjyfm1n5iia.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%2Fvwk2pybkfwjyfm1n5iia.png" alt="subscription" width="800" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As described in the &lt;a href="https://grafana.com/docs/loki/latest/clients/lambda-promtail/" rel="noopener noreferrer"&gt;Lambda-Promtail&lt;/a&gt; documentation you have to use a CloudWatch Logs subscription filter to read Lamba logs. As serverless hero Yan Cui stated in &lt;a href="https://theburningmonk.com/2018/07/centralised-logging-for-aws-lambda-revised-2018/" rel="noopener noreferrer"&gt;this article&lt;/a&gt;, the &lt;code&gt;Transform&lt;/code&gt; Lambda eats up your Lambda concurrency and adds additional costs. You could leverage that by using additional Kinesis streams, or you use the &lt;strong&gt;Lambda Telemetry API&lt;/strong&gt; as an Lambda extension.&lt;/p&gt;

&lt;p&gt;That makes this lighter architecture possible:&lt;/p&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%2Flvthsy387lda6o5wnm0e.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%2Flvthsy387lda6o5wnm0e.png" alt="subscription light" width="800" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Lambda Telemetry API
&lt;/h2&gt;

&lt;p&gt;With the Lambda Telemetry API, Lambda extensions can directly receive telemetry data from Lambda.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lambda Extensions
&lt;/h3&gt;

&lt;p&gt;With the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/runtimes-extensions-api.html" rel="noopener noreferrer"&gt;Lambda Extension API&lt;/a&gt;, you can use a Lambda Layer, which is called as an independent process in the execution environment and &lt;em&gt;can continue&lt;/em&gt; to run after the function invocation is fully processed.&lt;/p&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%2Fn78vx9vdfwo7lxnb6rhl.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%2Fn78vx9vdfwo7lxnb6rhl.png" alt="extension" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Lambda Telemetry API
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/telemetry-api.html" rel="noopener noreferrer"&gt;Telemetry API&lt;/a&gt; gets events from these telemetry streams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Platform&lt;/strong&gt; telemetry – Logs, metrics, and traces, which describe events and errors related to the execution environment runtime lifecycle, extension lifecycle, and function invocations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Function&lt;/strong&gt; logs – Custom logs that the Lambda function code generates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extension&lt;/strong&gt; logs – Custom logs that the Lambda extension code generates.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An extension app can get all logging output from a Lambda Function by subscribing to the &lt;code&gt;Function&lt;/code&gt; events. The event types are defined in the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/telemetry-schema-reference.html" rel="noopener noreferrer"&gt;developer guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By &lt;em&gt;not&lt;/em&gt; subscribing to Platform events, you will not get events like the &lt;code&gt;platform.start&lt;/code&gt; event, which creates the  &lt;code&gt;START&lt;/code&gt; log entries. You will only get the output from the Lambda Function itself. This way, the logs will be better parseable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grafana Labs Loki
&lt;/h2&gt;

&lt;p&gt;The Grafana tools are often used in containerized environment. So I will build the extension for &lt;a href="https://grafana.com/oss/loki/" rel="noopener noreferrer"&gt;Grafana Loki&lt;/a&gt;, which is the logging solution. &lt;/p&gt;

&lt;p&gt;Fun fact: My first approach was using &lt;a href="https://www.parseable.io/docs/quick-start" rel="noopener noreferrer"&gt;parseable&lt;/a&gt;. But it turned out that parseable is not capable of working with IAM roles at the moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  From Lambda Telemetry API Event to promtail
&lt;/h3&gt;

&lt;p&gt;You can push events into Loki with the promtail api or the newer Loki api. The api is described in the &lt;a href="https://grafana.com/docs/loki/latest/api/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. I will use the &lt;code&gt;/api/prom/push&lt;/code&gt; call.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Extension Code
&lt;/h2&gt;

&lt;p&gt;As AWS states in &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/telemetry-api.html#telemetry-api-creating-extensions" rel="noopener noreferrer"&gt;Creating extensions using the Telemetry API&lt;br&gt;
&lt;/a&gt; that it is recommended to use a compiled language such as Golang or Rust. So &lt;a href="https://www.go-on-aws.com/" rel="noopener noreferrer"&gt;of course&lt;/a&gt;, I am using GO.&lt;/p&gt;

&lt;p&gt;There are some extension examples  &lt;a href="https://github.com/aws-samples/aws-lambda-extensions" rel="noopener noreferrer"&gt;github&lt;/a&gt;. I am using &lt;code&gt;go-example-telemetry-api-extension&lt;/code&gt; as a starting point. You can see the code in my &lt;a href="https://github.com/megaproaktiv/Lambda-Telemetry-API-Loki/tree/main/loki_extension" rel="noopener noreferrer"&gt;Lambda-Telemetry-API-Loki&lt;/a&gt; repository.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who's calling who
&lt;/h3&gt;

&lt;p&gt;The extension works in these main steps:&lt;/p&gt;

&lt;p&gt;1) Register the extension with Extensions API&lt;br&gt;
2) Start an HTTP listener to receive events &lt;br&gt;
3) Subscribe to any of the types of Platform, Function, Extension&lt;br&gt;
4) Receive events and dispatch them to the target, here Loki&lt;/p&gt;
&lt;h4&gt;
  
  
  1) Register
&lt;/h4&gt;

&lt;p&gt;This is done via the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/runtimes-extensions-api.html#extensions-registration-api-a" rel="noopener noreferrer"&gt;&lt;em&gt;Register&lt;/em&gt;&lt;/a&gt; call of the extension api.&lt;/p&gt;

&lt;p&gt;See &lt;code&gt;extensionApi/client.go&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;`&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;baseUrl&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://%s/2020-01-01/extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AWS_LAMBDA_RUNTIME_API"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/register"&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;
&lt;span class="c"&gt;///&lt;/span&gt;
&lt;span class="n"&gt;httpRes&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;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpReq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2) Listener
&lt;/h4&gt;

&lt;p&gt;The struct is composed of an HTTP server and a queue. For performance reasons, events are not directly pushed to Loki but are queued up to the &lt;code&gt;DISPATCH_MIN_BATCH_SIZE&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;The Lambda extension gets its environment from the Lambda Function, so you set the values in the Lambda Function API.&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;type&lt;/span&gt; &lt;span class="n"&gt;TelemetryApiListener&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;httpServer&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;
    &lt;span class="c"&gt;// LogEventsQueue is a synchronous queue and is used to put the received log events to be dispatched later&lt;/span&gt;
    &lt;span class="n"&gt;LogEventsQueue&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;queue&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3) Subscribe
&lt;/h4&gt;

&lt;p&gt;In &lt;code&gt;telemetryApi/client.go&lt;/code&gt; the function &lt;code&gt;Subscribe&lt;/code&gt; calls the &lt;code&gt;baseUrl&lt;/code&gt; of the &lt;em&gt;telemetry&lt;/em&gt; extension:&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;baseUrl&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://%s/2022-07-01/telemetry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AWS_LAMBDA_RUNTIME_API"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The possible parameters are described in the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/telemetry-api-reference.html#telemetry-subscribe-api" rel="noopener noreferrer"&gt;Lambda Telemetry API reference&lt;/a&gt;.&lt;br&gt;
For example, if you expect high-volume data, you should tune the batch size and the buffering parameter of the subscribe call.&lt;/p&gt;

&lt;p&gt;Now that we have an extension, we need to build it &amp;amp; install it to Lambda Functions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Build &amp;amp; Install the Telemetry Layer
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://github.com/megaproaktiv/Lambda-Telemetry-API-Loki" rel="noopener noreferrer"&gt;repository&lt;/a&gt; I build the layer binaries with the help of the great &lt;a href="https://taskfile.dev" rel="noopener noreferrer"&gt;taskfile&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;loki_extension
task build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amd64 go build &lt;span class="nt"&gt;-o&lt;/span&gt; dist/extensions/grafana-loki-extension main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which creates an executable binary for amd64 architecture. If you use arm architecture, you have to change the GOARCH variable. The created binary is zipped and published as an Lambda Layer.&lt;br&gt;
To set the Layer ARN in the Lambda Function, I store the ARN.&lt;br&gt;
With the extension, you define the compatible runtimes &lt;code&gt;--compatible-runtimes&lt;/code&gt; for the Lambda Layer.&lt;/p&gt;

&lt;p&gt;The whole call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;aws lambda publish-layer-version &lt;span class="nt"&gt;--layer-name&lt;/span&gt; &lt;span class="s2"&gt;"grafana-loki-extension"&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; eu-central-1 &lt;span class="nt"&gt;--compatible-runtimes&lt;/span&gt; nodejs16.x go1.x python3.9 &lt;span class="nt"&gt;--zip-file&lt;/span&gt;  &lt;span class="s2"&gt;"fileb://extension.zip"&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"LayerVersionArn"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$ARN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;arn:aws:lambda:eu-central-1:012345679812:layer:grafana-loki-extension:1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the layer is installed in the current AWS account. We can use the Layer in Lambda Functions without changing one line of the function code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Test Architecture Setup
&lt;/h2&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%2Fp9k67998k4kr3xec0yww.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%2Fp9k67998k4kr3xec0yww.png" alt="Setup" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The setup has three components:&lt;/p&gt;

&lt;p&gt;1) The telemetry layer with the Loki extension&lt;br&gt;
2) A Grafana Loki containers and other Telemetry tools as docker-compose setup&lt;br&gt;
3) Test Lambda Functions for the Telemetry API written in  TypeScript, Python and Go&lt;/p&gt;
&lt;h3&gt;
  
  
  1) The telemetry Layer
&lt;/h3&gt;

&lt;p&gt;This is the Layer I installed. For the Lambda Function you reference the ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:lambda:eu-central-1:012345679812:layer:grafana-loki-extension:1&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2) Grafana Tools
&lt;/h3&gt;

&lt;p&gt;I am using a setup from the book &lt;a href="https://www.amazon.com/Cloud-Native-Observability-OpenTelemetry-visibility-combining/dp/1801077703" rel="noopener noreferrer"&gt;Cloud-Native Observability with OpenTelemetry&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is &lt;em&gt;not for production&lt;/em&gt;. For better setups, see the other articles of this series.&lt;/p&gt;

&lt;p&gt;The setup includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Opentelemetry-collector&lt;/li&gt;
&lt;li&gt;Prometheus&lt;/li&gt;
&lt;li&gt;Loki&lt;/li&gt;
&lt;li&gt;Promtail&lt;/li&gt;
&lt;li&gt;Grafana&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a test environment, we start the containers on a Cloud9 Server in a public network.&lt;/p&gt;

&lt;p&gt;2.1. Create a  Cloud 9 environment&lt;/p&gt;

&lt;p&gt;2.2. Add rules to the security group of the instance open to your IP, and open the Loki/Promtail api for Lambda IP addresses like:&lt;/p&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%2Fktv54pnkywjeadzz24b5.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%2Fktv54pnkywjeadzz24b5.png" alt="sg" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For production you would have an authentication setup or/and Lambda in a VPC in a private subnet, so that you got fixed IPs.&lt;/p&gt;

&lt;p&gt;2.3. Install Docker Compose on Cloud9:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/docker/compose/releases/download/1.23.1/docker-compose-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/docker-compose
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; +x /usr/local/bin/docker-compose
docker-compose &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;AWSReservedSSO_AWSAdministratorAccess_b58ba5bc1d953bb1:~/environment &lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;--version&lt;/span&gt;
docker-compose version 1.23.1, build b02f1306
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.4. Clone the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/megaproaktiv/Lambda-Telemetry-API-Loki.git
&lt;span class="nb"&gt;cd &lt;/span&gt;Lambda-Telemetry-API-Loki/grafana-container/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.5. Start the Grafana environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Creating network "grafana-container_observability" with the default driver
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.6. Open Grafana&lt;/p&gt;

&lt;p&gt;Now the Grafana tools should be running, so open the public IP of your Cloud9 instance on port &lt;code&gt;3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example: &lt;code&gt;http://3.67.195.192:3000/?orgId=1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see something like:&lt;/p&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%2Fc0m58vx78sr3nm13yd3s.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%2Fc0m58vx78sr3nm13yd3s.png" alt="Grafana" width="753" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3) Lambda Functions
&lt;/h3&gt;

&lt;p&gt;In the directory &lt;code&gt;lambda_telementry_api&lt;/code&gt; the Lambda Resources are defined with CDK.&lt;/p&gt;

&lt;p&gt;If you use another layer version, you have to adapt the url in &lt;code&gt;lib/lambda_telementry_api-stack.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extensionName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grafana-loki-extension&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;layerVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loki_ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3.67.195.192&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distpatch_min_batch_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lambdatelematryApiLayerArn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:lambda:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:layer:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;extensionName&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;layerVersion&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ltaLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LayerVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromLayerVersionArn&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ltalayer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lambdatelematryApiLayerArn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also set the Loki IP from the last step.&lt;/p&gt;

&lt;p&gt;Make sure you got docker running and deploy the CDK stack with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;task deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will build all Lambda Functions and deploy them.&lt;/p&gt;

&lt;p&gt;Now we have established a simpler logging setup:&lt;/p&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%2Fpb23jh6d42ehw6t2fmg5.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%2Fpb23jh6d42ehw6t2fmg5.png" alt="Extension" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the setup
&lt;/h2&gt;

&lt;p&gt;The Lambda functions take all PutObject events from an S3 Bucket and write the object key into a DynamoDB.&lt;/p&gt;

&lt;p&gt;There is a test script inside the &lt;code&gt;lambda_telementry_api&lt;/code&gt; directory, which puts object in the created bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/test/traffic.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop the script after approx. 20 copy calls.&lt;/p&gt;

&lt;p&gt;The output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;So 12 Feb 2023 14:42:17 CET
upload: ./readme.md to s3://lambdatelementryapistack-incoming0b397865-hjxvhc2842jy//test-4-0-0-
So 12 Feb 2023 14:42:18 CET
upload: ./readme.md to s3://lambdatelementryapistack-incoming0b397865-hjxvhc2842jy//test-4-0-1-
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CloudWatch logs of the Lambda Function now show the output from the extension also. The log level is set to verbose, so you can see many events:&lt;/p&gt;

&lt;h3&gt;
  
  
  Log events from the extension
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time="2023-02-12T13:42:18Z" level=info msg="[main] Starting the Telemetry API extension" pkg=main
time="2023-02-12T13:42:18Z" level=info msg="[main] Registering extension" pkg=main
time="2023-02-12T13:42:18Z" level=info msg="[client:Register] Registering using baseURLhttp://127.0.0.1:9001/2020-01-01/extension" pkg=extensionApi
time="2023-02-12T13:42:18Z" level=info msg="[client:Register] Registration success with extensionId 5b43e095-292c-4d3c-94d0-be9cea43c78d" pkg=extensionApi
time="2023-02-12T13:42:18Z" level=info msg="[main] Registation success with extensionId5b43e095-292c-4d3c-94d0-be9cea43c78d" pkg=main
time="2023-02-12T13:42:18Z" level=info msg="[main] Starting the Telemetry listener" pkg=main
time="2023-02-12T13:42:18Z" level=info msg="[listener:Start] Starting on addresssandbox:4323" pkg=telemetryApi
time="2023-02-12T13:42:18Z" level=info msg="[main] Subscribing to the Telemetry API" pkg=main
time="2023-02-12T13:42:18Z" level=info msg="[client:Subscribe] Subscribing using baseUrl:http://127.0.0.1:9001/2022-07-01/telemetry" pkg=telemetryApi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The queue waits for shutdown or if the &lt;code&gt;distpatch_min_batch_size&lt;/code&gt;is reached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time="2023-02-12T13:58:16Z" level=info msg="[listener:http_handler] logEvents received:1 LogEventsQueue length:2" pkg=telemetryApi
time="2023-02-12T14:02:18Z" level=info msg="[listener:http_handler] logEvents received:2 LogEventsQueue length:11" pkg=telemetryApi
time="2023-02-12T14:02:18Z" level=info msg="[dispatcher:Dispatch] Dispatching :11 log events" pkg=telemetryApi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Log events from the Platform
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INIT_START Runtime Version: python:3.9.v16  Runtime Version ARN: arn:aws:lambda:eu-central-1::runtime:07a48df201798d627f2b950f03bb227aab4a655a1d019c3296406f95937e2525
END RequestId: 1af7f8ab-94e7-4a7a-b3c8-db06eedf874b
REPORT RequestId: 1af7f8ab-94e7-4a7a-b3c8-db06eedf874b  Duration: 132.32 ms Billed Duration: 133 ms Memory Size: 1024 MB    Max Memory Used: 83 MB  Init Duration: 783.94 ms    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Log events from the Function
&lt;/h3&gt;

&lt;p&gt;The Python Lambda &lt;code&gt;lambda_telementry_api/lambda/py/app.py&lt;/code&gt; logs the DynamoDb Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;itemKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;putDynamoItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Table&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;itemKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the cloudwatch logs we see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{'ConsumedCapacity': {'TableName': 'items', 'CapacityUnits': 1.0}, 'ResponseMetadata': {'RequestId': 'DPKG7GR6V8G0SA2ATEPR2M4UB3VV4KQNSO5AEMVJF66Q9ASUAAJG', 'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'Server', 'date': 'Sun, 12 Feb 2023 13:42:19 GMT', 'content-type': 'application/x-amz-json-1.0', 'content-length': '62', 'connection': 'keep-alive', 'x-amzn-requestid': 'DPKG7GR6V8G0SA2ATEPR2M4UB3VV4KQNSO5AEMVJF66Q9ASUAAJG', 'x-amz-crc32': '2296128304'}, 'RetryAttempts': 0}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we should see the same event in Grafana.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query the Function logs in Grafana Loki / Grafana
&lt;/h2&gt;

&lt;p&gt;Loki works with labels. To find the Lambda Functions, the promtail client is initialized with the Function name as tag in &lt;code&gt;loki/promtail.go&lt;/code&gt;:&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;function_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AWS_LAMBDA_FUNCTION_NAME"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;labels&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"{source=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;source_name&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;,function=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;}"&lt;/span&gt;
    &lt;span class="n"&gt;lokiIp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LOKI_IP"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lokiIp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LOKI Ip undefined"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;promtail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientConfig&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;PushURL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;            &lt;span class="s"&gt;"http://"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;lokiIp&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;":3100/api/prom/push"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Labels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;             &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;BatchWait&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;BatchEntriesNumber&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SendLevel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="n"&gt;promtail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;PrintLevel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;promtail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ERROR&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;All Lambda Function event are tagged as &lt;code&gt;Source=Lambda&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the Grafana browser start Loki (2) with the explore icon (1):&lt;/p&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%2Frtf2us2lctr686vwd2ky.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%2Frtf2us2lctr686vwd2ky.png" alt="Explore Loki" width="494" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So after the first events arrived in Loki, you see these labels:&lt;/p&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%2F2yyt0nxfj0imzfz2nl2s.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%2F2yyt0nxfj0imzfz2nl2s.png" alt="Lambda Labels" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Label &lt;code&gt;source&lt;/code&gt; shows that we have &lt;code&gt;Lambda&lt;/code&gt;events and the three different &lt;code&gt;function&lt;/code&gt; tags let us query each of the Functions.&lt;/p&gt;

&lt;p&gt;When you query these labels, you get &lt;em&gt;only&lt;/em&gt; the Function logs, not the Platform or extension logs as in CLoudWatch:&lt;/p&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%2Fp4cdxuq2tvv02y70az4o.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%2Fp4cdxuq2tvv02y70az4o.png" alt="Query" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the extension is working and all &lt;em&gt;Function Logs&lt;/em&gt; are shipped to Loki!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The extension API and the telemetry extensions of Lambda open up possibilities for connecting telemetry data to AWS and non-AWS targets.&lt;/p&gt;

&lt;p&gt;If the extension is up and running, you have much more flexibility than with CloudWatch alone. On the other hand, Lambda is highly integrated with CloudWatch logs, and you have to configure no additional Layers to get that.&lt;/p&gt;

&lt;p&gt;So for the standard serverless App, the extensions should not be necessary. But for high volume or hybrid projects, it provides more functionality and saves costs because you do not need additional resources in the form of Log Functions.&lt;/p&gt;

&lt;p&gt;The main advantage is that logging Lambda Functions do not use up the Lambda concurrency. Also, you can decide which Log events you want to see.&lt;/p&gt;

&lt;p&gt;This article concludes the series about Serverless Observability.&lt;/p&gt;

&lt;p&gt;If you need consulting for your serverless project, don't hesitate to get in touch with the sponsor of this blog, &lt;a href="https://www.tecracer.com/kontakt/" rel="noopener noreferrer"&gt;tecRacer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more AWS development stuff, follow me on dev &lt;a href="https://dev.to/megaproaktiv"&gt;https://dev.to/megaproaktiv&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  See also
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/megaproaktiv/Lambda-Telemetry-API-Loki" rel="noopener noreferrer"&gt;Source code Lambda-Telemetry-API-Loki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://opentelemetry.io/docs/collector/configuration/" rel="noopener noreferrer"&gt;OpenTelemetry documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>saas</category>
      <category>productivity</category>
      <category>discuss</category>
      <category>startup</category>
    </item>
    <item>
      <title>Solved: go mod tidy: warning: "all" matched no packages / GO debugger does not work in VSCode</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Fri, 27 Jan 2023 08:11:09 +0000</pubDate>
      <link>https://forem.com/megaproaktiv/solved-go-mod-tidy-warning-all-matched-no-packages-40dg</link>
      <guid>https://forem.com/megaproaktiv/solved-go-mod-tidy-warning-all-matched-no-packages-40dg</guid>
      <description>&lt;p&gt;In just some projects &lt;code&gt;go mod tidy&lt;/code&gt; did not work.&lt;/p&gt;

&lt;p&gt;Now I figured out, why:&lt;/p&gt;

&lt;p&gt;If you are inside a working directory, which is a symbolic link, it fails with &lt;code&gt;warning: "all" matched no packages.&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you cd to the linked directory, it works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y5K8em4W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g7tbeh79btk0c2p8wtn8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y5K8em4W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g7tbeh79btk0c2p8wtn8.png" alt="go mod tidy" width="880" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"The expert is amazed and the layman is confused!" - Ancient german proverb! &lt;/p&gt;

&lt;p&gt;Update: This also happens with the VSCode debugger.&lt;br&gt;
Could be only a MAC M1 problem...&lt;/p&gt;

</description>
      <category>go</category>
      <category>devtools</category>
      <category>fail</category>
    </item>
    <item>
      <title>Serverless Spy Vs. Spy Chapter 3: X-Ray vs Jaeger - Send Lambda traces with open telemetry</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Tue, 20 Dec 2022 10:06:10 +0000</pubDate>
      <link>https://forem.com/aws-builders/serverless-spy-vs-spy-chapter-3-x-ray-vs-jaeger-send-lambda-traces-with-open-telemetry-583j</link>
      <guid>https://forem.com/aws-builders/serverless-spy-vs-spy-chapter-3-x-ray-vs-jaeger-send-lambda-traces-with-open-telemetry-583j</guid>
      <description>&lt;p&gt;In modern architectures, Lambda functions co-exist with containers. Cloud Native Observability is achieved with open telemetry. I show you how to send open telemetry traces from Lambda to a Jaeger  tracing server. Let's see how this compares to the X-Ray tracing service.&lt;/p&gt;

&lt;p&gt;As the Lambda setup with Typescript and Python already had a good coverage in &lt;a href="https://www.tecracer.com/blog/2022/12/spy/adot/"&gt;chapter 2&lt;/a&gt;, I will stick to GO here. The CDK code is easy to migrate. &lt;/p&gt;

&lt;h2&gt;
  
  
  Setting
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gk-kR0tA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqcmkovpqfci6g02apmq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gk-kR0tA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqcmkovpqfci6g02apmq.png" alt="Overview" width="880" height="587"&gt;&lt;/a&gt;)&lt;br&gt;
&lt;strong&gt;Architecture overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Lambda Function (2) sends traces to the jaeger backend with the &lt;a href="https://www.jaegertracing.io/docs/1.40/apis/#opentelemetry-protocol-stable"&gt;OpenTelemetry Protocol&lt;/a&gt;. Because we do not want to accept requests from the internet, Lambda has to run within the network of the VPC called &lt;code&gt;basevpc&lt;/code&gt;. This VPC is created (1) at first. The jaeger container announces its IP via the AWS Serviced-Discovery service. &lt;/p&gt;

&lt;p&gt;To access the frontend/UI of jaeger a Load Balancer is created between the internal jaeger service private IP and the internet.&lt;/p&gt;

&lt;p&gt;The CDK code, the application code and jaeger itself are written on GO. &lt;/p&gt;
&lt;h2&gt;
  
  
  Lambda
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Lambda Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2JmdFEia--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhea6rz5se2783qgyj3b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2JmdFEia--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhea6rz5se2783qgyj3b.png" alt="Lambda Resource" width="595" height="328"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;AWS Lambda Resources&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  1   lambdaPath := filepath.Join(path, "../dist/main.zip")
  2   adotLayer := lambda.LayerVersion_FromLayerVersionArn(this, aws.String("adotlayer"),
  3     aws.String("arn:aws:lambda:eu-central-1:901920570463:layer:aws-otel-collector-amd64-ver-0-62-1:1"))
  4   fn := lambda.NewFunction(this, aws.String("adotlambda"),
  5   &amp;amp;lambda.FunctionProps{
  6     Vpc: vpc,
  7     Handler: aws.String("main"),
  8     Runtime: lambda.Runtime_PROVIDED_AL2(),
  9     Tracing: lambda.Tracing_ACTIVE,
 10     Environment: &amp;amp;map[string]*string{
 11       "OPENTELEMETRY_COLLECTOR_CONFIG_FILE" : aws.String("/var/task/config.yml"),
 12       // "https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/"
 13       "OTEL_SERVICE_NAME" : aws.String("documentcounter"),
 14     },
 15     AllowPublicSubnet: aws.Bool(true),
 16     Layers: &amp;amp;[]lambda.ILayerVersion{
 17         adotLayer,
 18     },
 19     },
 20   )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have to define the following configuration, see &lt;a href="https://www.tecracer.com/blog/2022/12/spy/adot/"&gt;chapter 2&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Line 2:3 - The Lambda Layer for the otel collector &lt;/li&gt;
&lt;li&gt;Line 6 -  run in the VPC&lt;/li&gt;
&lt;li&gt;Line 1 - Set the configuration file location&lt;/li&gt;
&lt;li&gt;Line 16 - Activate the layer&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Lambda Code
&lt;/h3&gt;

&lt;p&gt;In the application you have to do: &lt;/p&gt;

&lt;p&gt;1. Configure the middleware to send traces&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;otelaws.AppendMiddlewares(&amp;amp;cfg.APIOptions)
ClientDDB = dynamodb.NewFromConfig(cfg)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. Propagate the context through all functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From main:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;tp&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;xrayconfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;//...&lt;/span&gt;
&lt;span class="n"&gt;lambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otellambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InstrumentHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HandleRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xrayconfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithRecommendedOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;to HandleRequest
&lt;/li&gt;
&lt;/ul&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;HandleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s3Event&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S3Event&lt;/span&gt;&lt;span class="p"&gt;)&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="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;//...&lt;/span&gt;
&lt;span class="n"&gt;putItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;s3input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;to putitem
&lt;/li&gt;
&lt;/ul&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;putItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;itemID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="c"&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;ClientDDB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bpm-Amyu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a39j303justok497fgfk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bpm-Amyu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a39j303justok497fgfk.png" alt="otel flow" width="880" height="780"&gt;&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;In the app, at the end an s3 listobjects is performed, so that you have two AWS services in the traces.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://www.tecracer.com/blog/2022/12/spy/adot/"&gt;chapter 2&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;Now Lambda could send traces, so we need a target. I chose Jaeger, an open-source, end-to-end distributed tracing, originally provided by Uber Technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jaeger Installation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  VPC
&lt;/h3&gt;

&lt;p&gt;We provide a VPN to run the ECS service - just a VPC with a private subnet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fargate Service
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_-OkwFPC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06qvod5ezf0h76kh8uxm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_-OkwFPC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06qvod5ezf0h76kh8uxm.png" alt="Jaeger service" width="880" height="495"&gt;&lt;/a&gt;)&lt;br&gt;
&lt;strong&gt;The JAEGER service&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The front end will be provided on port &lt;code&gt;16686&lt;/code&gt;, the OTEL request will go to port &lt;code&gt;4317&lt;/code&gt; via gRPC. All jaeger ports are described in the &lt;a href="https://www.jaegertracing.io/docs/1.40/deployment"&gt;deployment&lt;/a&gt; part of the jaeger documentation.&lt;/p&gt;

&lt;p&gt;To access the jager front end with a DNS name, you have to have a domain. So change the following configurations in &lt;code&gt;jaeger/cluster.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var SERVICE_NAME = "jaeger"
var NAMESPACE = "otel.letsbuild-aws.com"
var HOSTED_ZONE_ID = "Z042038724KH99T9LFKK6"
var DNS_NAME = "service.letsbuild-aws.com"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, I have created a subdomain "service.letsbuild-aws.com" for the Load Balancer. The NAMESPACE is used for service discovery. You do not need a real domain for service discovery.&lt;/p&gt;

&lt;p&gt;To get jaeger up and running, there is an all-in-one image we use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jaegertracing/all-in-one:1.39.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The jaeger container can be configured via the environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"SPAN_STORAGE_TYPE":      aws.String("memory"),
"COLLECTOR_OTLP_ENABLED": aws.String("true"),
"LOG_LEVEL":              aws.String("debug"),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To keep it (almost) simple, the storage is set to &lt;code&gt;memory&lt;/code&gt;. In production, you could use Cassandra, elasticsearch and other backends. As stated in the jaeger &lt;a href="https://www.jaegertracing.io/docs/1.40/cli/"&gt;documentation&lt;/a&gt;, all CLI parameters can be set via ENV variables. To be able to receive otlp data, its enabled.&lt;/p&gt;

&lt;p&gt;The management ui and otlp ports are configured for the container:&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;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddContainer&lt;/span&gt;&lt;span class="p"&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;"jaegerContainer"&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;ecs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerDefinitionOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerImage_FromRegistry&lt;/span&gt;&lt;span class="p"&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;"jaegertracing/all-in-one:1.39.0"&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;span class="n"&gt;ContainerName&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;"jaeger-all"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="c"&gt;//...&lt;/span&gt;
        &lt;span class="n"&gt;PortMappings&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PortMapping&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;ContainerPort&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MANAGEMENT_PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;HostPort&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;MANAGEMENT_PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Protocol_TCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c"&gt;// management&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;ContainerPort&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;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4317&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;HostPort&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;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4317&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Protocol_TCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c"&gt;// "otel-grpc"&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="c"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the &lt;code&gt;jaeger/cluster.go&lt;/code&gt; file for the complete source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect Lambda to Jaeger
&lt;/h2&gt;

&lt;p&gt;On the jaeger side a namespace is configured:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace := awsservicediscovery.NewPrivateDnsNamespace(this, aws.String("oteltrace-namespace"),
    &amp;amp;awsservicediscovery.PrivateDnsNamespaceProps{
        Name:        aws.String(NAMESPACE),
        Description: aws.String("DNS service discovery subdomain"),
        Vpc:         vpc,
    },
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This creates an entry in the &lt;em&gt;private&lt;/em&gt; domain &lt;code&gt;otel.letsbuild-aws.com&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YqVNtNTl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dpb1c4ntvnhgd6rxli68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YqVNtNTl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dpb1c4ntvnhgd6rxli68.png" alt="DNS" width="880" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the Lambda side the first thing is to tell the adot Layer, where to find the config file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"OPENTELEMETRY_COLLECTOR_CONFIG_FILE" : aws.String("/var/task/config.yml"),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Because Lambda apps are deployed into the directory &lt;code&gt;/var/task&lt;/code&gt; on the Lambda micro-vm, you have to prepend the path &lt;code&gt;/var/task&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You find the file here: &lt;code&gt;app/config.yml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The configuration is added to the Lambda deployment package:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o ../dist/main main/main.go
chmod +x ../dist/main
mv ../dist/main ../dist/bootstrap
cp config.yml ../dist
cd ../dist &amp;amp;&amp;amp; zip main.zip bootstrap config.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The lambda package build script&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;In the configuration, we have three parts&lt;/p&gt;

&lt;p&gt;1) The local receiver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;grpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4317&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It receives the traces. &lt;/p&gt;

&lt;p&gt;2) The exporter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jaeger.otel.letsbuild-aws.com:4317&lt;/span&gt;
    &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;insecure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the dns name from the awsservicediscovery is used for the &lt;code&gt;ENDPOINT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;3) The pipelines&lt;/p&gt;

&lt;p&gt;Now incoming receiver is piped to the outgoing exporter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;traces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Details are described in the &lt;a href="https://opentelemetry.io/docs/collector/configuration/"&gt;OpenTelemetry documentation&lt;/a&gt;. As stated in &lt;a href="https://www.tecracer.com/blog/2022/12/spy/adot/"&gt;chapter 2&lt;/a&gt;, not all configurations are valid here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compare X-Ray UI to Jager UI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  X-Ray now
&lt;/h3&gt;

&lt;p&gt;As the collector is not configured for X-Ray traces, we just see the data from the Lambda &lt;em&gt;service&lt;/em&gt;, not the function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c30zac51--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w0tjz1vuym7uw5bgpoyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c30zac51--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w0tjz1vuym7uw5bgpoyh.png" alt="X-Ray service only" width="459" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Switch the configuration back to x-rays:&lt;/p&gt;

&lt;p&gt;1) Change app/config.yml&lt;/p&gt;

&lt;p&gt;Samples for the configurations are provided in &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app/config-otel.yml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app/config-xray.yml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) Deploy app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;app
task fastdeploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then some traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
task traffic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we see all nodes in the X-Ray Map view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bXXhoaeB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hd5mczo2asofn1wsrue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bXXhoaeB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hd5mczo2asofn1wsrue.png" alt="Xray all services" width="880" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And some traces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XBQzCl2g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b5jio1x98uvctc7pzqh5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XBQzCl2g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b5jio1x98uvctc7pzqh5.png" alt="X-Ray Trace Map" width="880" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;X-Ray Trace Map&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Jaeger/Otel
&lt;/h3&gt;

&lt;p&gt;Switch the configuration back to otel and deploy Lambda app again. After creating some traffic, you see traces in the jaeger ui.&lt;/p&gt;

&lt;p&gt;Access the jaeger UI from the loadbalancer dns entry or your domain name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--45vYnWj8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3n0qnccscc8rdb5nehzc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--45vYnWj8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3n0qnccscc8rdb5nehzc.png" alt="Jaeger base" width="880" height="445"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Jaeger Trace Map&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) Choose Service &lt;em&gt;documentcounter&lt;/em&gt; here&lt;br&gt;
    This is the name I set with the environment variable&lt;br&gt;
    &lt;code&gt;OTEL_SERVICE_NAME&lt;/code&gt;, configured in the Lambda Resource.&lt;br&gt;
2) The button [Find Traces] shows a graphical view (4) and the single traces   &lt;/p&gt;

&lt;p&gt;Click on a trace (3) to see the detail view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BrSGShVI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dpex8ru5ngea765r9aw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BrSGShVI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dpex8ru5ngea765r9aw7.png" alt="Jaeger Timeline" width="880" height="455"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Jaeger Timeline&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Comparing both trace maps we notice the missing nodes with the AWS service icons.&lt;/p&gt;

&lt;p&gt;Comparing the timelines, you see that the segments &lt;strong&gt;otellambda AWS::Lambda&lt;/strong&gt; and &lt;strong&gt;otellamba AWS::Lambda::Function&lt;/strong&gt; only appear in X-Ray, not on jaeger.&lt;/p&gt;

&lt;p&gt;That is because only the Lambda &lt;em&gt;Function&lt;/em&gt; sends traces, not the Lambda Service. In the "Cloud-Native" container world, usually, it is assumed that the container is already running. So the startup time is not interesting.  In Lambda the micro-vm is started, when a request hits a cold start. If that happens often, it may affect your overall latency, so you want to have data. You may get the init duration also from the Lambda Logs. If you need information from the Lambda &lt;em&gt;Resource&lt;/em&gt;, you might use the Lambda extensions and the AWS Lambda Telemetry API, which I will cover in the last chapter.&lt;/p&gt;

&lt;p&gt;The detail information are almost the same:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TPcsjRDW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k09v51yq5wy9c6833ytx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TPcsjRDW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k09v51yq5wy9c6833ytx.png" alt="putitem detail" width="880" height="307"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Is there a winner?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Functionality
&lt;/h3&gt;

&lt;p&gt;If you have to decide whether to use X-Ray or other services for your traces, ADOT is the more flexible choice. It provides more support from various sources.&lt;/p&gt;

&lt;p&gt;For services that have a large AWS part, the X-Ray service provide some more functionality like creating nodes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cost
&lt;/h3&gt;

&lt;p&gt;It depends on your metrics!&lt;/p&gt;
&lt;h4&gt;
  
  
  Otel open source tracing e.g. jaeger
&lt;/h4&gt;

&lt;p&gt;I have seen some other posts, which stated that an extra tracing service would be cheaper, "because it is open source". If you compare the costs the tco have some more parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Costs of the running container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price in eu-central-1&lt;br&gt;
per vCPU per hour   $0.04656&lt;br&gt;
per GB per hour $0.00511&lt;br&gt;
With 2 vCPU | 4 GB &lt;/p&gt;

&lt;p&gt;Which would be 62.01 €/month&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cost of storage: depends on backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the production environment, you would like to set up an application load balancer with cognito authentication with additional costs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tvnSdY5f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkh21tqd1jlx7x1noxez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tvnSdY5f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkh21tqd1jlx7x1noxez.png" alt="production setup" width="880" height="541"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  X-Ray
&lt;/h4&gt;

&lt;p&gt;The X-Ray server costs are $5.00 per 1 million traces in eu-central-1. You can also adapt the sample rate to not have a trace with each call.&lt;/p&gt;
&lt;h3&gt;
  
  
  Operations
&lt;/h3&gt;

&lt;p&gt;The telemetry infrastructure setup is done only once. Once you have it running, there should be not much to do.&lt;/p&gt;

&lt;p&gt;With X-Ray, there is no additional operational cost.&lt;/p&gt;
&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;Because you provide the jaeger container yourself, you can adapt the size to the speed you need. In my tests the jaeger frontend seemed very much faster than the X-Ray aka CloudWatch Service Map.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With the &lt;a href="https://github.com/open-telemetry/opentelemetry-lambda/tree/main/go/sample-apps"&gt;sample apps&lt;/a&gt; from the opentelemetry-lambda repository the Lambda part itself was easy to implement. What took me some time was to provide the jaeger Fargate service with IaC ouside of an k8s environment. But with ECS and ServiceDiscovery that was easy in the end. This should be even more simple in an EKS environment with the &lt;a href="https://github.com/jaegertracing/helm-charts"&gt;jaegertracing helm-charts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using something else as tracing solution instead of X-Ray not looks like a good choice for AWS serverless projects. &lt;br&gt;
But if you have a container solution up and running, otel would be a good choice for an environment, where container traces and Lambda traces are stored together.&lt;/p&gt;
&lt;h2&gt;
  
  
  See also
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jaegertracing.io/"&gt;JAEGER&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://opentelemetry.io/docs/collector/configuration/"&gt;OpenTelemetry documentation&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/megaproaktiv/adot-otelstarter"&gt;Source code ADOT-otelstarter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need consulting for your serverless project, don't hesitate to get in touch with the sponsor of this blog, &lt;a href="https://www.tecracer.com/kontakt/"&gt;tecRacer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more AWS development stuff, follow me on dev &lt;a href="https://dev.to/megaproaktiv"&gt;https://dev.to/megaproaktiv&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Appendix: Quick Walkthrough
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Clone repository
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/megaproaktiv/adot-otelstarter.git
&lt;span class="nb"&gt;cd &lt;/span&gt;adot-otelstarter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Set region
export AWS_REGION=yourregion,  e.g.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eu-central-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;If CDK is not bootstrapped:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  task bootstrap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Create VPC
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  task jaeger:deploy-vpc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Set Domain and Service configuration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Edit   &lt;code&gt;jaeger/cluster.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  var SERVICE_NAME &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"jaeger"&lt;/span&gt;
  var NAMESPACE &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"otel.letsbuild-aws.com"&lt;/span&gt;
  var HOSTED_ZONE_ID &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Z042035555KH99T9LFKK6"&lt;/span&gt;
  var DNS_NAME &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"service.letsbuild-aws.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create ECS cluster with jaeger service
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  task jaeger:deploy-jaeger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Deploy Lambda Resources and function
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  task deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: because of the ENI this could take a few minutes&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Traffic
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  task traffic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>serverless</category>
      <category>telemetry</category>
      <category>aws</category>
      <category>jaeger</category>
    </item>
    <item>
      <title>First surprising Contact with ChatGP: Can it solve simple AWS SDK tasks?</title>
      <dc:creator>Gernot Glawe</dc:creator>
      <pubDate>Sun, 18 Dec 2022 08:21:41 +0000</pubDate>
      <link>https://forem.com/megaproaktiv/first-surprising-contact-with-chatgp-can-it-solve-simple-aws-sdk-tasks-3iak</link>
      <guid>https://forem.com/megaproaktiv/first-surprising-contact-with-chatgp-can-it-solve-simple-aws-sdk-tasks-3iak</guid>
      <description>&lt;p&gt;I heard so many things, so I wanted to give &lt;strong&gt;&lt;a href="https://chat.openai.com/chat" rel="noopener noreferrer"&gt;https://chat.openai.com/chat&lt;/a&gt;&lt;/strong&gt; a GO.&lt;/p&gt;

&lt;p&gt;Can it help me in programming or even replace me?&lt;/p&gt;

&lt;p&gt;My question:&lt;/p&gt;

&lt;p&gt;"show me the code to list all CloudFormation stack names in an AWS account in GO with SDK v2 in clean code"&lt;/p&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%2Fdpnl6wwp91ynlfpago9z.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%2Fdpnl6wwp91ynlfpago9z.png" alt="Chat" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Does the code work out of the box?
&lt;/h2&gt;

&lt;p&gt;Short answer: No.&lt;br&gt;
But it would save me google search time.&lt;/p&gt;
&lt;h2&gt;
  
  
  What did I have to change?
&lt;/h2&gt;

&lt;p&gt;A surprisingly small amount of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff --git a/main.go b/main.go
index 01d6286..2b02055 100644
--- a/main.go
+++ b/main.go
@@ -4,34 +4,33 @@ import (
        "context"
        "fmt"

-       "github.com/aws/aws-sdk-go-v2/config"
+       "github.com/aws/aws-sdk-go-v2/aws"
        "github.com/aws/aws-sdk-go-v2/service/cloudformation"
-       "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
 )

 func main() {
        // Initialize the AWS configuration
-       cfg, err :=  config.LoadDefaultConfig(context.Background())
+       cfg, err := aws.NewConfig()
        if err != nil {
                panic("Unable to create AWS configuration: " + err.Error())
        }

        // Create a new CloudFormation client
-       cfnSvc := cloudformation.NewFromConfig(cfg)
+       cfnSvc := cloudformation.New(cfg)

        // Set up the input parameters for the ListStacks API call
        input := &amp;amp;cloudformation.ListStacksInput{
-               StackStatusFilter: []types.StackStatus{
-                       types.StackStatusCreateComplete,
... some more states
-                       types.StackStatusRollbackFailed,
-                       types.StackStatusUpdateComplete,
-                       types.StackStatusUpdateRollbackComplete,
-                       types.StackStatusUpdateRollbackFailed,
+               StackStatusFilter: []cloudformation.StackStatus{
+                       cloudformation.StackStatusCreateComplete,
+                       cloudformation.StackStatusUpdateRollbackFailed,
                },
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So with a few commands and some patches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go mod init test1
vi main.go
go mod tidy
go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Indeed something to watch in the future!&lt;br&gt;
Try &lt;a href="https://chat.openai.com/" rel="noopener noreferrer"&gt;https://chat.openai.com/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@thomascpark?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Thomas Park&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/surprise?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And ChatGP. &lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
  </channel>
</rss>
