Go의 Azure 함수 사용자 지정 처리기: 로깅

소개



Azure 최근announced general availability of custom handlers for Azure functions . 사용자 지정 핸들러는 함수 호스트에서 HTTP 요청을 수신하는 경량 웹 서버입니다. 이 요청을 수신하도록 웹 서버를 설정할 수 있는 모든 프로그래밍 언어 또는 런타임을 사용자 지정 처리기로 사용할 수 있습니다. 이 게시물에서는 Go의 사용자 지정 핸들러, 특히 이 사용자 지정 핸들러에서 로깅을 고려합니다.

사용자 지정 핸들러에서 로깅할 때의 제한 사항



사용자 지정 핸들러를 사용하는 데는 몇 가지 제한 사항이 있으며 이 게시물에서 집중적으로 다룰 내용은 로깅입니다. 로깅은 공식적인 제한 사항으로 나열되지 않지만 프로덕션 구현을 위한 사용자 지정 처리기를 탐색하기 시작하면 이 사실을 빠르게 발견할 수 있습니다. 현재 솔루션을 사용하면 함수 호스트에 대한 응답으로 반환하는 문자열 배열에 모든 로그를 저장할 수 있습니다. 그러면 이러한 로그가 로그 대상에 삽입됩니다.

이것이 왜 제한 사항입니까? 우려할 수 있는 것은 크게 두 가지입니다.
  • 각 로그의 메시지 필드만 제어할 수 있습니다. 다른 필드에 영향을 미치거나 사용자 지정 필드를 포함할 수 없습니다. 여기에는 심각도 필드가 포함되며 이는 모든 로그의 심각도가 동일함을 의미합니다.
  • 모든 로그의 타임스탬프가 거의 동일합니다. 이는 어레이의 호스트로 반환된 다음 배치의 로그 대상에 삽입되기 때문입니다. 내가 말할 수 있는 한 순서는 보존되지만 나는 그것에 의존하지 않을 것입니다.

  • 어떻게 로그인해야 합니까?



    Azure는 향후 사용자 지정 처리기에 대한 로깅 기능을 확장할 수 있지만 그때까지 옵션은 무엇입니까?

    간단한 fmt.Println(...) 로 여전히 stdout에 로그인할 수 있지만 이렇게 하면 로그에서 얻을 수 있는 일부 컨텍스트가 손실됩니다. 어떤 맥락입니까? 예를 들어 호출 ID와 로그를 생성한 함수의 이름입니다.

    로그 메시지가 알려진 형식을 따르는 경우에도 로그 배열 솔루션을 사용하고 메시지 필드를 검색하여 원하는 로그를 찾을 수 있습니다. 로그가 구조화된 JSON인 경우 Kusto 쿼리 언어의 함수를 사용하여 구문 분석을 처리할 수 있으며 이는 아래 텍스트에서 설명하겠습니다.

    사용자 지정 JSON 로깅을 사용하는 예제 솔루션



    이 샘플의 전체 코드는 https://github.com/mattias-fjellstrom/go-custom-handler-logging에서 사용할 수 있습니다. 이 샘플에서는 enableForwardingHttpRequesttrue 로 설정했으며 이는 Functions 호스트가 HTTP 요청을 수정하지 않고 내 핸들러로 전달함을 의미합니다.
    main.go의 내 진입점은 다음과 같습니다.

    package main
    
    import (
      "log"
      "net/http"
      "os"
    
      "github.com/mattias-fjellstrom/go-custom-handler/src/handlers"
    )
    
    func main() {
      port, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
      if !exists {
        port = "8080"
      }
    
      mux := http.NewServeMux()
      mux.HandleFunc("/api/greeting", handlers.GreetingHandler)
    
      log.Fatal(http.ListenAndServe(":"+port, mux)
    }
    


    그리고 내 커스텀 로거를 초기화하는 내 핸들러 함수

    package handlers
    
    import (
      "net/http"
      "time"
      "encoding/json"
    
      "github.com/mattias-fjellstrom/go-custom-handler-logging/src/logging"
    )
    
    // GreetingHandler handles GET requests to /api/greeting
    func GreetingHandler(w http.ResponseWriter, r *http.Request) {
      logger := logging.NewLogger()
    
      logger.Info("Log before a five seconds sleep")
      time.Sleep(5 * time.Second)
      logger.Info("Log after a five seconds sleep")
      logger.Warning("This is a warning log")
      logger.Error("This is an error log")
    
      response := "Hello!"
      w.Header().Set("Content-Type", "application/json")
      w.WriteHeader(http.StatusOK)
      json.NewEncoder(w).Encode(response)
    }
    


    마지막으로 내 맞춤 로거는

    package logging
    
    import (
      "time"
    
      "github.com/sirupsen/logrus"
    )
    
    func NewLogger() logrus.FieldLogger {
      logger := logrus.New()
      logger.SetLevel(logrus.InfoLevel)
      jsonFormatter := logrus.JSONFormatter{
        TimestampFormat: time.RFC3339Nano,
        FieldMap: logrus.FieldMap{
          logrus.FieldKeyTime: "timestamp",
          logrus.FieldKeyMsg: "message",
          logrus.FieldKeyLevel: "level",     
        },
      }
      logger.SetFormatter(&jsonFormatter)
      return logger
    }
    


    저는 Azure 기능을 위한 우수한 VS Code 확장을 사용하여 이 기능을 배포합니다. 배포되면 몇 번 호출한 다음 내 Application Insights 리소스로 이동하고 다음 쿼리를 실행합니다.

    traces 
    | where message startswith "{"
    | extend d=parse_json(message)
    | project d.timestamp, d.level, d.message
    

    where message startswith "{" 문은 JSON으로 기록된 메시지만 포함하는 간단한 방법입니다. 호스트가 몇 가지 JSON 메시지도 기록하기 때문에 이상적이지 않습니다. extend d=parse_json(message) 문은 message 필드를 JSON으로 읽고 JSON 로그 개체에서 project(즉, 지정된 필드만 표시) 필드를 허용합니다. 샘플 응답은 아래 이미지에 나와 있습니다.



    결과를 보면 위에 나열된 두 가지 제한 사항에 대한 솔루션이 있다는 것이 분명합니다. 원하는 필드는 무엇이든 만들 수 있고 로그 사이의 상대적 시간이 보존됩니다. 더 많은 필드로 로그 형식을 확장하여 원하는 컨텍스트를 얻을 수 있습니다.

    좋은 웹페이지 즐겨찾기