forked from google/certificate-transparency-go
-
Notifications
You must be signed in to change notification settings - Fork 0
RA-7777: Implement logging of timing events in the trillian ctfe #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
98e6994
RA-7777: Incorporated logging of timing for a request from the http r…
himaschal 3072f96
RA-7777: modified logging format to json
himaschal 7f58232
RA-7777: Added some basic tests for the logger
himaschal f05fe4d
RA-7777: Added some tests for the middleware
himaschal 45fc8bc
Remove custom message. It was used for testing purposes only
himaschal 195347e
Span id should not be propagated
himaschal 72f2549
RA-7777: Fix a codeql issue. Sanitize the log message to prevent poss…
himaschal 4e7add6
RA-7777: Sanitize the transaction_id to some degree of confidence
himaschal 4ccdfd3
Revert "RA-7777: Sanitize the transaction_id to some degree of confid…
himaschal 7775e66
Revert "RA-7777: Fix a codeql issue. Sanitize the log message to prev…
himaschal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package logging | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/google/uuid" | ||
"github.com/sirupsen/logrus" | ||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/metadata" | ||
) | ||
|
||
type contextKey string | ||
|
||
const ( | ||
CtxKeyTxID contextKey = "transaction_id" | ||
CtxKeySpanID contextKey = "span_id" | ||
) | ||
|
||
var log = logrus.New() | ||
|
||
func init() { | ||
log.Formatter = &logrus.JSONFormatter{ | ||
TimestampFormat: "2006-01-02T15:04:05.000Z07:00", | ||
} | ||
} | ||
|
||
func generateUUID() string { | ||
return uuid.New().String() | ||
} | ||
|
||
func WithContext(r *http.Request) context.Context { | ||
txID := r.Header.Get("X-Transaction-ID") | ||
if txID == "" { | ||
txID = generateUUID() | ||
} | ||
|
||
spanID := generateUUID() | ||
ctx := context.WithValue(r.Context(), CtxKeyTxID, txID) | ||
ctx = context.WithValue(ctx, CtxKeySpanID, spanID) | ||
return ctx | ||
} | ||
|
||
func WithGRPCContext(ctx context.Context) context.Context { | ||
// First check if there's already a transaction_id in the context (from HTTP) | ||
txID, ok := ctx.Value(CtxKeyTxID).(string) | ||
if !ok || txID == "" { | ||
// If not, try to get it from gRPC metadata | ||
txID = getFromMetadata(ctx, "X-Transaction-ID") | ||
if txID == "" { | ||
txID = generateUUID() | ||
} | ||
} | ||
|
||
// Check for span_id in context first | ||
spanID, ok := ctx.Value(CtxKeySpanID).(string) | ||
if !ok || spanID == "" { | ||
// Always generate a new span_id for this service | ||
// No longer checking metadata since span_id is not propagated | ||
spanID = generateUUID() | ||
} | ||
|
||
ctx = context.WithValue(ctx, CtxKeyTxID, txID) | ||
ctx = context.WithValue(ctx, CtxKeySpanID, spanID) | ||
return ctx | ||
} | ||
|
||
func getFromMetadata(ctx context.Context, key string) string { | ||
md, ok := metadata.FromIncomingContext(ctx) | ||
if !ok { | ||
return "" | ||
} | ||
values := md.Get(key) | ||
if len(values) > 0 { | ||
return values[0] | ||
} | ||
return "" | ||
} | ||
|
||
// PropagateToGRPC adds the transaction_id from the context to gRPC metadata | ||
// This ensures transaction correlation across service boundaries | ||
// Note: span_id is NOT propagated - each service generates its own span_id | ||
func PropagateToGRPC(ctx context.Context) context.Context { | ||
txID := ctx.Value(CtxKeyTxID) | ||
|
||
if txID == nil { | ||
return ctx | ||
} | ||
|
||
mdMap := map[string]string{ | ||
"X-Transaction-ID": txID.(string), | ||
} | ||
// Deliberately NOT propagating span_id - each service gets its own | ||
|
||
md := metadata.New(mdMap) | ||
return metadata.NewOutgoingContext(ctx, md) | ||
} | ||
|
||
func LogWithContext(ctx context.Context, eventID string, msg string, fields map[string]interface{}) { | ||
lf := logrus.Fields{ | ||
"event_id": eventID, | ||
} | ||
|
||
// Safely extract transaction_id and span_id | ||
if txID := ctx.Value(CtxKeyTxID); txID != nil { | ||
lf["transaction_id"] = txID | ||
} | ||
if spanID := ctx.Value(CtxKeySpanID); spanID != nil { | ||
lf["span_id"] = spanID | ||
} | ||
|
||
for k, v := range fields { | ||
lf[k] = v | ||
} | ||
log.WithFields(lf).Info(msg) | ||
} | ||
|
||
func LogTiming(ctx context.Context, r *http.Request, status int, elapsed time.Duration) { | ||
elapsedInMsStr := fmt.Sprintf("%dms", elapsed.Milliseconds()) | ||
LogWithContext(ctx, "timing", "request completed", map[string]interface{}{ | ||
"path": r.URL.Path, | ||
"method": r.Method, | ||
"status": status, | ||
"elapsed": elapsedInMsStr, | ||
}) | ||
} | ||
|
||
func UnaryServerInterceptor() grpc.UnaryServerInterceptor { | ||
return func( | ||
ctx context.Context, | ||
req interface{}, | ||
info *grpc.UnaryServerInfo, | ||
handler grpc.UnaryHandler, | ||
) (interface{}, error) { | ||
ctx = WithGRPCContext(ctx) | ||
start := time.Now() | ||
resp, err := handler(ctx, req) | ||
elapsed := time.Since(start) | ||
LogWithContext(ctx, "timing", "gRPC call completed", map[string]interface{}{ | ||
"method": info.FullMethod, | ||
"status": statusCodeFromError(err), | ||
"elapsed": elapsed.Milliseconds(), | ||
}) | ||
return resp, err | ||
} | ||
} | ||
|
||
func statusCodeFromError(err error) string { | ||
if err == nil { | ||
return "OK" | ||
} | ||
return err.Error() | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.