Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Progate_PathにおけるNew_Relic連携と活用事例

Avatar for Naoya Uda Naoya Uda
October 08, 2024
85

 Progate_PathにおけるNew_Relic連携と活用事例

NRUG (New Relic User Group) 名古屋 Vol.0 の登壇資料です

Avatar for Naoya Uda

Naoya Uda

October 08, 2024
Tweet

Transcript

  1. Progate PathにおけるNew Relic連携と活用事例 2024/10/04 @NRUG (New Relic User Group) 名古屋

    Vol.0 株式会社Progate Path Group ソフトウェアエンジニア 宇多直也
  2. Progate Path の紹介 6 • Progate Pathはプロダクト開発ができる人になってもらう学習サービス 実装 企画 設計

    実際に作り上げる 評価 まずはここがきちんと1人でできるかが問われる 仮 説 実 行 検 証 どうやって 実現するか考える 欲しいものを 考える 欲しかったものがで きたかを 確認する
  3. • Path apiはgo + gRPCを利用しているため、New Relic公式のintegrationを用いて RPCごとにtransactionを可視化している • https://github.com/newrelic/go-agent のgRPC

    Interceptorを噛ませることで簡単 にtransactionを可視化できる server := grpc.NewServer( grpc.UnaryInterceptor(nrgrpc.UnaryServerInterceptor(app, nrgrpc.WithStatusHandler(codes.OK, nrgrpc.ErrorInterceptorStatusHandler), grpc.StreamInterceptor(nrgrpc.StreamServerInterceptor(app)), ) Error調査時に役立っているもの : Transaction 15
  4. logger := logrus.New() nrApp, err := newrelic.NewApplication() // newrelicのapplicationを作る if

    err != nil { logger.Fatalf("unable to create New Relic Application: %s", err) } // loggerのformatterをnewrelicのformatterに変更することで、logしたときにnewrelicにもログが送信されるようになる logger.SetFormatter(nrlogrus.NewFormatter(nrApp, &logrus.JSONFormatter{})) // loggerをentryに変換し、request-scoped contextに埋め込む interceptor := grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(logger)) ステップ1: ContextにNew Relicに対応した Loggerを仕込む 18
  5. func CreateLoggingFieldsUnaryServerInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info

    *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { user := auth.UserInfoContext(ctx) if !user.Anonymous { // context内のLog entryにaccountSubject(user id)を仕込む ctxlogrus.AddFields(ctx, logrus.Fields{ "accountSubject": user.AccountSubject, }) } return handler(ctx, req) } } ステップ2: Request発生時にinterceptorでユーザー情報を Context内Log Entryに埋め込む 19
  6. func contextLogger(ctx context.Context) *logrus.Entry { // context内からfieldを埋め込んだloggerを取り出して、改めてcontextを詰めて返す return ctxlogrus.Extract(ctx).WithContext(ctx) }

    func SomeRequest(ctx context.Context, req *SomeRequest) (resp *SomeResponse, err error) { something, err := something(req) if err != nil { // withErrorでerrorを渡すとerror自体がlogに出力される ContextLogger(ctx).WithError(err).Error("some error happened") } } ステップ3: ContextからLoggerを取り出して logする 20
  7. • Synthetic Monitoringによる外形監視 • やっていること ◦ シンプルなpingによるhealthcheck ◦ サービスに対するログインができるかチェック ▪

    JavaScriptでコードを書ける ▪ WebDriver + seleniumを使ったブラウザ操作を書いて動かしている New Relicを用いて実施していること 26
  8. await $webDriver.get('https://app.path.progate.com') const email = await $webDriver.findElement($selenium.By.css('input[type="email"]')) await email.sendKeys('[email protected]') const

    password = await $webDriver.findElement($selenium.By.css('input[type="password"]')) await password.sendKeys('password') const submitButton = await $webDriver.findElement($selenium.By.css('button[type="submit"]') await submitButton.click() await $webDriver.wait($selenium.until.urlIs('https://app.path.progate.com/dashboard')) JavaScriptで動作を書ける 27
  9. • Summary ◦ 見たい情報(Golden signalsやApdex score)が概ね表示されている ◦ Golden Signals ▪

    Response time ▪ Throughput ▪ Error Rate ◦ Apdex score ▪ request全体に対するユーザーの満足度 • Transaction ◦ 主にWeb Transaction(requestが始まってからresponseを返すまで) • ECSコンテナ(主にapi)のmemory/cpu使用率 主にAPMで確認しているもの 29
  10. func Something(ctx context.Context, req *SomeRequest) (resp *SomeResponse, err error) {

    // contextからnew rewlicのrequestのtransactionを取り出す txn := newrelic.FromContext(ctx) fooSegment := txn.StartSegment("foo segment start") foo() fooSegment.End() barSegment := txn.StartSegment("bar segment start") bar() barSegment.End() buzSegment := txn.StartSegment("buz segment start") buz() buzSegment.End() } Request(Transaction)の中にsegmentを埋め込む 31