go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/impl/prod/logger_new.go (about) 1 // Copyright 2022 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build go1.12 16 // +build go1.12 17 18 package prod 19 20 import ( 21 "context" 22 "encoding/hex" 23 "fmt" 24 "math/rand" 25 "net/http" 26 "os" 27 "regexp" 28 29 "go.chromium.org/luci/common/logging" 30 "go.chromium.org/luci/common/logging/sdlogger" 31 ) 32 33 // Disable calls to LogFlush RPC from within google.golang.org/appengine guts, 34 // this RPC is not available on second gen runtime. This code requires running 35 // https://github.com/golang/appengine/commit/07f9b0860d0 or newer, which 36 // unfortunately wasn't released as a tagged version. 37 func init() { 38 if err := os.Setenv("LOG_TO_LOGSERVICE", "0"); err != nil { 39 panic(fmt.Sprintf("Error setting LOG_TO_LOGSERVICE=0: %s", err)) 40 } 41 } 42 43 var logSink = &sdlogger.Sink{Out: os.Stdout} 44 45 // useLogging adds a logging.Logger implementation to the context which emits 46 // structured logs to stdout in a form GAE runtime can parse. 47 func useLogging(c context.Context, req *http.Request) context.Context { 48 traceID, spanID := traceAndSpan(req) 49 return logging.SetFactory(c, sdlogger.Factory(logSink, sdlogger.LogEntry{ 50 TraceID: traceID, 51 SpanID: spanID, 52 Operation: &sdlogger.Operation{ID: genUniqueID(32)}, 53 }, nil)) 54 } 55 56 var traceContextRe = regexp.MustCompile(`^(\w+)/(\d+)(?:;o=[01])?$`) 57 58 func traceAndSpan(req *http.Request) (string, string) { 59 headers := req.Header["X-Cloud-Trace-Context"] 60 if len(headers) < 1 { 61 return "", "" 62 } 63 matches := traceContextRe.FindAllStringSubmatch(headers[0], -1) 64 if len(matches) < 1 || len(matches[0]) < 3 { 65 return "", "" 66 } 67 traceID := matches[0][1] 68 spanID := matches[0][2] 69 projectID := os.Getenv("GOOGLE_CLOUD_PROJECT") 70 return fmt.Sprintf("projects/%s/traces/%s", projectID, traceID), spanID 71 } 72 73 func genUniqueID(n int) string { 74 b := make([]byte, n/2) 75 rand.Read(b) // note: doesn't have to be crypto random 76 return hex.EncodeToString(b) 77 }