مشاهده AWS Lambda با Golang و Datadog
ابزار دقیق کد برای مشاهده پذیری از قبل یکی از مهم ترین کارهایی است که یک توسعه دهنده می تواند برای خود “آینده” خود انجام دهد. و ابزار دقیق برای مشاهده AWS Lambda با Datadog و Golang در واقع بسیار سادهتر از آن چیزی است که میتوانید تصور کنید. پس این به چه معناست؟
تعریف
مشاهده پذیری توانایی اندازه گیری وضعیت یک سیستم در هر زمان معین، درک نحوه عملکرد و رفتار آن است و سطح مناسبی از دید را برای هر عملیات در صورت نیاز به بازرسی فراهم می کند. 3 ستونی که من بیشتر با آنها آشنا هستم معیارها، ردیابی ها و گزارش ها هستند. من اظهار نمی کنم که در این موضوع متخصص هستم، اما می دانم که به چه معناست که بتوانم خدمات خود و خدمات تیمم را در مقیاس مشاهده کنم.
اول از همه، بسیار مهم است که ابزار دقیق از قبل به درستی انجام شود (یا حتی سعی شده است انجام شود). با انجام این کار، هنگامی که کارها وارد مرحله تولید می شوند، داده هایی را در اختیار دارید تا بتوانید مشکلات را در زمان و در صورت وقوع حل کنید و علاوه بر نظارت بر آن سرویس ها، به توافق نامه های سطح خدمات و اهداف سطح خدمات آنها دست پیدا کنید.
این پست قرار نیست به صورت تئوری در مورد نحوه انجام این کار صحبت کند، اما به طور خاص به شما نشان می دهد که چگونه آن را راه اندازی و اجرا کنید تا بتوانید ببینید که مشاهده لامبدا با Golang و Datadog واقعاً چگونه است. من در چند سال گذشته از طرفداران بزرگ Datadog بودهام و صادقانه بگویم که در حال حاضر با برخی از بهترین ابزارهای حرفهام کار میکنم. امیدواریم این به شما کمک کند زیرا می خواهید کاری مشابه انجام دهید
همه ما به چه چیزی نیاز داریم؟
فرض کنید مشکلی در تولید رخ می دهد و شما هزاران رویداد را در ساعت دریافت می کنید و نمی توانید به راحتی ردیابی کنید که بلوک توهین آمیز کد چیست. تصور کنید وقتی یک سیستم بسیار توزیع شده دارید که در آن بسیاری از کارهای Lambdas یا ECS یا EKS Tasks در حال اجرا هستند و مطمئن نیستید که کدام یک از آنها واقعاً مشکل را ایجاد می کند، این کار حتی سخت می شود.
تنها چیزی که می دانید این است که یک مشتری با تیم پشتیبانی شما تماس گرفته است و آنها مشکل را به شما اطلاع داده اند. و بدتر هم میشود که همیشه این اتفاق نمیافتد، اما در واقع بیشتر سوزنی در انبار کاه است. وقتی هزاران مشتری دارید، ممکن است به نظر مهم نرسد… فقط به آنها بگویید refresh را بزنند.
اما اگر چیزی در زیر وجود داشته باشد که باعث شود بارها و بارها به همان شکل اتفاق بیفتد، چه؟ دلهره آور به نظر می رسد. و می تواند باشد. اما اگر کارهایی را از قبل انجام دهید، لازم نیست اینطور باشد. به متریک ها، ردیابی ها و گزارش ها برگردیم و بیایید کمی معیارها را کنار بگذاریم و فقط روی گزارش ها تمرکز کنیم.
سطوح سیاهههای مربوط به Golang
ابتدا، باید مطمئن شوید که به درستی در سطح مناسب با مقدار مناسبی از محتوا ثبت نام می کنید. بیایید نمونه برداری و چه چیزی را از جدول خارج کنیم و فقط در مورد اینکه در چه سطحی باید چیزها را ارسال کنید بحث کنیم. من شخصاً در تولید دوست دارم موارد را در سطح ERROR ارسال کنم، اما همیشه دوست دارم که بتوانم سطح گزارش خود را به سرعت از طریق یک متغیر محیطی تغییر دهم. هنگام استفاده از CDK من معمولاً چنین کاری را انجام می دهم
new GoFunction(this, `SampleFunc`, {
entry: path.join(__dirname, `../src/source`),
functionName: `func-name`,
environment: {
"DD_FLUSH_TO_LOG": "true",
"DD_TRACE_ENABLED": "true",
"LOG_LEVEL": getLogLevel(props.stage) // this nugget right here
},
});
با اضافه کردن در LOG_LEVEL
متغیر محیطی برای این لامبدا، من توانایی تغییر آن را دارم اگر بخواهم در صورت تقاضا چیزی کمی پرمخاطب تر را مجبور کنم.
مثال زیر یک مثال ساده است، و میتوانید منطق «پیشفرض» بیشتری اضافه کنید، اما این سطح گزارش را از متغیر ارسال شده در آن تنظیم میکند.
lib.SetLevel(os.Getenv("LOG_LEVEL"))
برای مثال، من از کتابخانه بسیار محبوب logrus استفاده می کنم و سپس فرمت کننده گزارش را به صورت JSON تنظیم می کنم.
package main
import (
"context"
"os"
ddlambda "github.com/DataDog/datadog-lambda-go"
log "github.com/sirupsen/logrus"
)
func init() {
log.SetFormatter(&log.JSONFormatter{
PrettyPrint: false,
})
lib.SetLevel(os.Getenv("LOG_LEVEL"))
}
func main() {
lambda.Start(ddlambda.WrapFunction(handler, lib.DataDogConfig()))
}
func handler(ctx context.Context, event interface{}) error {
// you'd NOT use interface{} as the event
return nil
}
خروجی AWS Lambda Logging
آنچه در بالا انجام می شود این است که کتابخانه log را برای خروجی JSON تنظیم می کند و در Cloudwatch نوشته می شود. من معمولاً Log Retention خود را در Cloudwatch روی فقط یک روز تنظیم می کنم زیرا قرار است همیشه همه چیز را به Datadog ارسال کنم. این همان چیزی است که خروجی در Datadog به نظر می رسد. همانطور که از ویژگی های رویداد در سمت راست می توانید، همه چیز خوب و دسته بندی شده است. نکته بسیار جالب در مورد استفاده از JSON این است که Datadog بسیار زیبا را برای شما نمایش می دهد.
برای خروجی عناصر اضافی، با logrus از تابع ثبت withFields استفاده می کنم.
log.WithFields(
log.Fields{
"somethingInJson": theObject,
}).Debug("Logging out the object")
پس این خیلی باحاله ورود به Datadog. اما چگونه این اتفاق می افتد؟ من قصد ندارم در اینجا به بررسی نحوه انجام این کار با DD بپردازم، اما با خیال راحت اسناد آنها را در این مورد مطالعه کنید. این خارق العاده است. فقط بدانید که گزینه هایی دارید. برنامه های افزودنی Lambda، Forwarders یا وارد شده در منابع.
مشاهده آثار
ردیابی در برنامهها تنها راهی است که شما میتوانید کد خود را ابزار کنید تا درخواست یا رویداد یا هر چیزی که درخواست شما را راهاندازی کرده است در یک «زمینه» جمعآوری شود. ردیابی زمینه اصلی است که در آن همه موارد فرعی مرتبط هستند. اگر در یک دقیقه 1000 درخواست دریافت کنید، 1000 ردیابی خواهید داشت.
سپس در داخل هر یک از آن ردیابی ها چیزهایی به نام Spans خواهید داشت. Spans ها را نیز می توان تو در تو قرار داد تا هر تابع یا بلوکی که اجرا می کنید ویژگی های خاصی در مورد آن دریافت کند که مشخص کننده
- نام
- زمان شروع شد
- مدت زمان
این به زمانبندی کارها کمک میکند، علاوه بر اینکه میتوانید این عملیات خاص را گروهبندی کنید. باز هم، هنگام استفاده از Lambda با Golang و Datadog، ابزارسازی این کار را به خوبی انجام می دهد و کتابخانه Datadog Go آن را بسیار ساده می کند.
بیایید ابتدا نگاهی به نحوه نشان دادن این بیاندازیم. به یاد داشته باشید، Traces دارای Spans است و Spans می تواند دهانه فرزند در داخل دهانه والد داشته باشد
اولین چیز این است که نمودار شعله یک راه خوب برای مشاهده دهانه ها است. کاری که Datadog برای من انجام می دهد در زیر آورده شده است
### دهانه
همانطور که می بینید، Span سطح بالا، اجرای خود لامبدا است. سپس در داخل آن محدوده، یک تماس با DynamoDB و سپس یک تماس با SQS برای قرار دادن یک رویداد در صف دریافت کردم. همچنین به درصد زمانی که هر بازه از کل طول می کشد و سپس مدت زمانی که هر بازه نشان می دهد توجه کنید. هنگامی که به صورت عمودی به عنوان یک لیست مشاهده می شود، دوباره اینجاست
پس چگونه این کار را انجام دادم؟ دهانه سطح بالا به این شکل است
func handler(ctx context.Context, event interface{}) error {
span, _ := tracer.SpanFromContext(ctx)
// more code here
}
و ردیابی کتابخانه های AWS نیز ساده است. شما باید علاوه بر بسته بندی کتابخانه مشتری، زمینه را نیز در درخواست لحاظ کنید. برای DynamoDB در اینجا نحوه انجام این کار آمده است. به بسته بندی با کتابخانه Datadog توجه کنید
package lib
import (
"context"
"fmt"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/bix-digital/golang-fhir-models/fhir-models/fhir"
awstrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws"
)
// NewDynamoDBClient inits a DynamoDB session to be used throughout the services
func NewDynamoDBClient() *dynamodb.DynamoDB {
c := &aws.Config{
Region: aws.String("us-west-2"),
}
sess := session.Must(session.NewSession(c))
sess = awstrace.WrapSession(sess,
awstrace.WithAnalytics(true),
awstrace.WithAnalyticsRate(1.0))
return dynamodb.New(sess)
}
// in another file or func
// make a call to DynaomDB
_, err = d.db.PutItemWithContext(ctx, itemInput)
خیلی ساده، درست است؟ ردیابیها و گسترههای فردی همگی سیمکشی شدهاند و اگر از راهنماییهای DD پیروی کنید که چگونه وارد حساب خود شوید، آن نمودارهای شعله و فهرستهای دهانه زیبا را دریافت میکنید. اگر به طور تصادفی درخواست های HTTP را ردیابی می کنید، مقاله ای دارم که باید در ادامه به آن نگاه کنید
همه را به هم وصل می کند
بنابراین ما Traces (spans) و Logs را داریم. به یاد داشته باشید که گفتم بعداً معیارها را پوشش خواهیم داد. اما در تولید، اگر ردیابی/فضاها و گزارشهای سیمکشی داشته باشم، میتوانم لاگهایی را که با خطا ثبت شدهاند فیلتر کنم و به راحتی میتوانم مؤلفه متخلف را بیابم، چه کارهای Lambda یا ECS (یا چیز دیگری). مشاهده آن Lambdas با Datadog با استفاده از Golang lib این کار را بسیار ساده می کند…
func handler(ctx context.Context, event interface{}) error {
span, _ := tracer.SpanFromContext(ctx)
newCtx := context.WithValue(ctx, "correlationId", event.MetaDetails.CorrelationId)
log.WithFields(
log.Fields{
"event": event,
"span_id": span.Context().SpanID(),
"trace_id": span.Context().TraceID(),
"correlationId": newCtx.Value("correlationId"),
}).Debugf("Logging out the event")
}
هنگامی که ما یک span جدید به دست آوردیم، به هر دهانه یک span id توسط DD اختصاص داده می شود و سپس در داخل آن span، ما به خود trace id دسترسی داریم. اگر چیزهایی را با span و trace id از سیستم خارج کنید، اتصالی را که می خواهید دریافت می کنید. و در برنامه Datadog، میتوانید به راحتی مشاهده کنید که این گزارشها با گستره داده شده مرتبط هستند و بالعکس. در صورت تمایل می توانید از نمایشگر گزارش شروع کرده و به سمت APM بچرخانید. نمای ساده این زیر که گزارشهای سطح INFO و DEBUG مرتبط با این ردیابی را نشان میدهد
آخرین نکته ای که در مورد همه اینها باید توجه داشت. ممکن است در کد بالا این فیلد به نام “correlationId” را مشاهده کنید که در حال خروج از سیستم است. چرا؟؟؟ ساده. تصور کنید که یک رویداد چیزی را به سمت بالا راه اندازی می کند و یک رویداد در 12 تابع، 8 صف و 3 جریان حرکتی قرار می گیرد. آیا خوب نیست که بتوانیم تاخیر کلی یک رویداد را ردیابی کنیم؟ یا حتی ردیابی شکست در آن یک رویداد خاص؟ اگر خطایی به کاربر نشان داده شد و “traceId” او نشان داده شد، می توانید آن تراکنش خاص را پیدا کرده و عیب یابی کنید. و با Datadog می توانید از این یک “وجه” از عبارت log استفاده کنید و آن را فیلتر کنید. برای مطالعه بیشتر، این مقاله ممکن است محل خوبی برای شروع باشد.
تکمیل مشاهده لامبدا با Datadog و Golang
جزئیات زیادی در این مورد وجود دارد، اما امیدواریم که برخی از الگوها و تنظیمات برای مشاهده AWS Lambdas نشان داده شده باشد، با Golang و Datadog برای انجام 2 مورد از 3 جزء کلیدی Observability. سیاههها، ردیابی ها و معیارها. من ممکن است در آینده مقاله ای در مورد متریک و چرایی مفید بودن آنها بنویسم. اما اگر سیستمهای توزیعشده مبتنی بر ابر را انجام میدهید، به نظر من حداقل داشتن گزارشها و ردیابیها و حداقل اتصال آنها به یکدیگر نقطه شروع خوبی به شما میدهد. قابلیت مشاهده رایگان نیست. شما باید کد خود را ابزارسازی کنید، اما زمانی که هر نوع حجمی دارید، با اولین شماره ای که در تولید دارید، بیشتر از آن برای خودش هزینه می کند.