feat: switch to semconv

This commit is contained in:
Kevin Franklin Kim 2025-10-08 15:43:32 +02:00
parent 80d236f6a2
commit b4b775681a
No known key found for this signature in database
25 changed files with 310 additions and 156 deletions

View File

@ -7,6 +7,7 @@ import (
"net/http/httptest"
"strings"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.uber.org/zap"
"github.com/foomo/keel/log"
@ -58,7 +59,7 @@ func (s *ServiceHTTP) Start(ctx context.Context) error {
ip = "0.0.0.0"
}
fields = append(fields, log.FNetHostIP(ip), log.FNetHostPort(port))
fields = append(fields, log.Attributes(semconv.ServerAddress(ip), semconv.ServerPortKey.String(port))...)
}
s.l.Info("starting http test service", fields...)

View File

@ -18,22 +18,22 @@ const (
JSONKey = "json"
)
// FNum - returns zap field
// FNum creates a zap.Field with a given number under the key "num".
func FNum(num int) zap.Field {
return zap.Int(NumKey, num)
}
// FName - returns zap field
// FName creates a zap.Field with a given string under the key "name".
func FName(name string) zap.Field {
return zap.String(NameKey, name)
}
// FValue - returns zap field
// FValue creates a zap.Field with a given value under the key "value".
func FValue(value interface{}) zap.Field {
return zap.String(ValueKey, fmt.Sprintf("%v", value))
}
// FJSON - returns zap field
// FJSON creates a zap.Field with a given value under the key "json".
func FJSON(v interface{}) zap.Field {
if out, err := json.Marshal(v); err != nil {
return zap.String(JSONKey+"_error", err.Error())

View File

@ -5,24 +5,32 @@ import (
)
const (
// Deprecated: use semconv messaging attributes instead.
CodeInstanceKey = "code_instance"
CodePackageKey = "code_package"
CodeMethodKey = "code_method"
CodeLineKey = "code_line"
// Deprecated: use semconv messaging attributes instead.
CodePackageKey = "code_package"
// Deprecated: use semconv messaging attributes instead.
CodeMethodKey = "code_method"
// Deprecated: use semconv messaging attributes instead.
CodeLineKey = "code_line"
)
// Deprecated: use semconv messaging attributes instead.
func FCodeInstance(v string) zap.Field {
return zap.String(CodeInstanceKey, v)
}
// Deprecated: use semconv messaging attributes instead.
func FCodePackage(v string) zap.Field {
return zap.String(CodePackageKey, v)
}
// Deprecated: use semconv messaging attributes instead.
func FCodeMethod(v string) zap.Field {
return zap.String(CodeMethodKey, v)
}
// Deprecated: use semconv messaging attributes instead.
func FCodeLine(v int) zap.Field {
return zap.Int(CodeLineKey, v)
}

View File

@ -7,28 +7,37 @@ import (
)
const (
DurationKey = "duration"
DurationSecKey = "duration_sec"
DurationMinKey = "duration_min"
// DurationKey - generic duration attribute
DurationKey = "duration"
// DurationSecKey - duration in seconds
DurationSecKey = "duration_sec"
// DurationMinKey - duration in minutes
DurationMinKey = "duration_min"
// DurationHourKey - duration in hours
DurationHourKey = "duration_hour"
)
// FDuration creates a zap.Field with a given time.Duration converted to milliseconds under the key "duration".
func FDuration(duration time.Duration) zap.Field {
return zap.Float64(DurationKey, float64(duration)/float64(time.Millisecond))
return zap.Int64(DurationKey, duration.Milliseconds())
}
// FDurationSec creates a zap.Field with a given time.Duration converted to seconds under the key "duration_sec".
func FDurationSec(duration time.Duration) zap.Field {
return zap.Float64(DurationSecKey, float64(duration)/float64(time.Second))
return zap.Float64(DurationSecKey, duration.Seconds())
}
// FDurationMin creates a zap.Field with a given time.Duration converted to minutes under the key "duration_min".
func FDurationMin(duration time.Duration) zap.Field {
return zap.Float64(DurationMinKey, float64(duration)/float64(time.Minute))
return zap.Float64(DurationMinKey, duration.Minutes())
}
// FDurationHour creates a zap.Field with a given time.Duration converted to hours under the key "duration_hour".
func FDurationHour(duration time.Duration) zap.Field {
return zap.Float64(DurationHourKey, float64(duration)/float64(time.Hour))
return zap.Float64(DurationHourKey, duration.Hours())
}
// FDurationFn returns a function that returns a zap.Field with a given time.Duration converted to milliseconds under the key "duration".
func FDurationFn() func() zap.Field {
start := time.Now()

View File

@ -5,110 +5,109 @@ import (
)
const (
// HTTPServerNameKey represents the name of the service handling the request
// Deprecated: use semconv messaging attributes instead.
HTTPServerNameKey = "http_server_name"
// HTTPMethodKey represents the HTTP request method.
// Deprecated: use semconv messaging attributes instead.
HTTPMethodKey = "http_method"
// HTTPTargetKey represents the full request target as passed in a HTTP
// request line or equivalent, e.g. "/path/12314/?q=ddds#123".
// Deprecated: use semconv messaging attributes instead.
HTTPTargetKey = "http_target"
// HTTPHostKey represents the value of the HTTP host header.
// Deprecated: use semconv messaging attributes instead.
HTTPHostKey = "http_host"
// HTTPStatusCodeKey represents the HTTP response status code.
// Deprecated: use semconv messaging attributes instead.
HTTPStatusCodeKey = "http_status_code"
// HTTPUserAgentKey represents the Value of the HTTP User-Agent header sent by the client.
// Deprecated: use semconv messaging attributes instead.
HTTPUserAgentKey = "http_user_agent"
// HTTPClientIPKey represents the IP address of the original client behind all proxies,
// if known (e.g. from X-Forwarded-For).
// Deprecated: use semconv messaging attributes instead.
HTTPClientIPKey = "http_client_ip"
// HTTPRequestContentLengthKey represents the size of the request payload body in bytes.
// Deprecated: use semconv messaging attributes instead.
HTTPRequestContentLengthKey = "http_read_bytes"
// HTTPWroteBytesKey represents the size of the response payload body in bytes.
// Deprecated: use semconv messaging attributes instead.
HTTPWroteBytesKey = "http_wrote_bytes" // #nosec
// HTTPSchemeKey represents the URI scheme identifying the used protocol.
// Deprecated: use semconv messaging attributes instead.
HTTPSchemeKey = "http_scheme"
// HTTPFlavorKey represents the Kind of HTTP protocol used.
// Deprecated: use semconv messaging attributes instead.
HTTPFlavorKey = "http_flavor"
// HTTPRequestIDKey represents the HTTP request id if known (e.g. from X-Request-ID).
// Deprecated: use semconv messaging attributes instead.
HTTPRequestIDKey = "http_request_id"
// HTTPSessionIDKey represents the HTTP session id if known (e.g. from X-Session-ID).
// Deprecated: use semconv messaging attributes instead.
HTTPSessionIDKey = "http_session_id"
// HTTPTrackingIDKey represents the HTTP tracking id if known (e.g. from X-Tracking-ID).
// Deprecated: use semconv messaging attributes instead.
HTTPTrackingIDKey = "http_tracking_id"
// HTTPRefererKey identifies the address of the web page (i.e., the URI or IRI), from which the resource has been requested.
// Deprecated: use semconv messaging attributes instead.
HTTPRefererKey = "http_referer"
)
// Deprecated: use semconv messaging attributes instead.
func FHTTPServerName(id string) zap.Field {
return zap.String(HTTPServerNameKey, id)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPRequestID(id string) zap.Field {
return zap.String(HTTPRequestIDKey, id)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPSessionID(id string) zap.Field {
return zap.String(HTTPSessionIDKey, id)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPTrackingID(id string) zap.Field {
return zap.String(HTTPTrackingIDKey, id)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPRequestContentLength(bytes int64) zap.Field {
return zap.Int64(HTTPRequestContentLengthKey, bytes)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPWroteBytes(bytes int64) zap.Field {
return zap.Int64(HTTPWroteBytesKey, bytes)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPStatusCode(status int) zap.Field {
return zap.Int(HTTPStatusCodeKey, status)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPTarget(target string) zap.Field {
return zap.String(HTTPTargetKey, target)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPClientIP(clientIP string) zap.Field {
return zap.String(HTTPClientIPKey, clientIP)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPFlavor(flavor string) zap.Field {
return zap.String(HTTPFlavorKey, flavor)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPScheme(scheme string) zap.Field {
return zap.String(HTTPSchemeKey, scheme)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPUserAgent(userAgent string) zap.Field {
return zap.String(HTTPUserAgentKey, userAgent)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPReferer(host string) zap.Field {
return zap.String(HTTPRefererKey, host)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPHost(host string) zap.Field {
return zap.String(HTTPHostKey, host)
}
// Deprecated: use semconv messaging attributes instead.
func FHTTPMethod(name string) zap.Field {
return zap.String(HTTPMethodKey, name)
}

View File

@ -1,11 +0,0 @@
package log
import (
"go.opentelemetry.io/otel/attribute"
)
const (
KeelServiceTypeKey = attribute.Key("keel.service.type")
KeelServiceNameKey = attribute.Key("keel.service.name")
KeelServiceInstKey = attribute.Key("keel.service.inst")
)

View File

@ -1,65 +1,89 @@
package log
import (
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.uber.org/zap"
)
const (
MessagingSystemKey = "messaging_system"
MessagingDestinationKey = "messaging_destination"
MessagingDestinationKindKey = "messaging_destination_kind"
MessagingProtocolKey = "messaging_protocol"
MessagingProtocolVersionKey = "messaging_protocol_version"
MessagingURLKey = "messaging_url"
MessagingMessageIDKey = "messaging_message_id"
MessagingConversationIDKey = "messaging_conversation_id"
MessagingMessagePayloadSizeBytesKey = "messaging_message_payload_size_bytes"
// Deprecated: use semconv messaging attributes instead.
MessagingSystemKey = "messaging_system"
// Deprecated: use semconv messaging attributes instead.
MessagingDestinationKey = "messaging_destination"
// Deprecated: use semconv messaging attributes instead.
MessagingDestinationKindKey = "messaging_destination_kind"
// Deprecated: use semconv messaging attributes instead.
MessagingProtocolKey = "messaging_protocol"
// Deprecated: use semconv messaging attributes instead.
MessagingProtocolVersionKey = "messaging_protocol_version"
// Deprecated: use semconv messaging attributes instead.
MessagingURLKey = "messaging_url"
// Deprecated: use semconv messaging attributes instead.
MessagingMessageIDKey = "messaging_message_id"
// Deprecated: use semconv messaging attributes instead.
MessagingConversationIDKey = "messaging_conversation_id"
// Deprecated: use semconv messaging attributes instead.
MessagingMessagePayloadSizeBytesKey = "messaging_message_payload_size_bytes"
// Deprecated: use semconv messaging attributes instead.
MessagingMessagePayloadCompressedSizeBytesKey = "messaging_message_payload_compressed_size_bytes"
)
// Deprecated: use semconv messaging attributes instead.
type MessagingDestinationKind string
const (
// Deprecated: use semconv messaging attributes instead.
MessagingDestinationKindQueue MessagingDestinationKind = "queue"
// Deprecated: use semconv messaging attributes instead.
MessagingDestinationKindTopic MessagingDestinationKind = "topic"
)
// Deprecated: use semconv messaging attributes instead.
func FMessagingSystem(value string) zap.Field {
return zap.String(MessagingSystemKey, value)
}
// Deprecated: use semconv.MessagingDestinationName instead.
func FMessagingDestination(value string) zap.Field {
return zap.String(MessagingDestinationKey, value)
return Attribute(semconv.MessagingDestinationName(value))
}
// Deprecated: use semconv messaging attributes instead.
func FMessagingDestinationKind(value MessagingDestinationKind) zap.Field {
return zap.String(MessagingDestinationKindKey, string(value))
}
// Deprecated: use semconv messaging attributes instead.
func FMessagingProtocol(value string) zap.Field {
return zap.String(MessagingProtocolKey, value)
}
// Deprecated: use semconv messaging attributes instead.
func FMessagingProtocolVersion(value string) zap.Field {
return zap.String(MessagingProtocolVersionKey, value)
}
// Deprecated: use semconv messaging attributes instead.
func FMessagingURL(value string) zap.Field {
return zap.String(MessagingURLKey, value)
}
// Deprecated: use semconv.MessagingMessageID instead.
func FMessagingMessageID(value string) zap.Field {
return zap.String(MessagingMessageIDKey, value)
return Attribute(semconv.MessagingMessageID(value))
}
// Deprecated: use semconv.MessagingMessageConversationID instead.
func FMessagingConversationID(value string) zap.Field {
return zap.String(MessagingConversationIDKey, value)
return Attribute(semconv.MessagingMessageConversationID(value))
}
// Deprecated: use semconv messaging attributes instead.
func FMessagingMessagePayloadSizeBytes(value string) zap.Field {
return zap.String(MessagingMessagePayloadSizeBytesKey, value)
}
// Deprecated: use semconv messaging attributes instead.
func FMessagingMessagePayloadCompressedSizeBytes(value string) zap.Field {
return zap.String(MessagingMessagePayloadCompressedSizeBytesKey, value)
}

View File

@ -1,21 +1,23 @@
package log
import (
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.uber.org/zap"
)
const (
// NetHostIPKey represents the local host IP. Useful in case of a multi-IP host.
// Deprecated: use semconv messaging attributes instead.
NetHostIPKey = "net_host_ip"
// NetHostPortKey represents the local host port.
// Deprecated: use semconv messaging attributes instead.
NetHostPortKey = "net_host_port"
)
// Deprecated: use semconv messaging attributes instead.
func FNetHostIP(ip string) zap.Field {
return zap.String(NetHostIPKey, ip)
return Attribute(semconv.HostIP(ip))
}
// Deprecated: use semconv messaging attributes instead.
func FNetHostPort(port string) zap.Field {
return zap.String(NetHostPortKey, port)
}

View File

@ -5,58 +5,53 @@ import (
)
const (
// PeerServiceKey represents the ServiceNameKey name of the remote service.
// Should equal the actual `service.name` resource attribute of the remote service, if any.
// Deprecated: use semconv messaging attributes instead.
PeerServiceKey = "peer_service"
// Deprecated: use semconv messaging attributes instead.
ServiceTypeKey = "service_type"
// Deprecated: use semconv messaging attributes instead.
ServiceNameKey = "service_name"
// Deprecated: use semconv messaging attributes instead.
ServiceMethodKey = "service_method"
// Deprecated: use semconv messaging attributes instead.
ServiceNamespaceKey = "service_namespace"
// Deprecated: use semconv messaging attributes instead.
ServiceInstanceIDKey = "service_instance.id"
// Deprecated: use semconv messaging attributes instead.
ServiceVersionKey = "service_version"
)
// Deprecated: use semconv messaging attributes instead.
func FPeerService(name string) zap.Field {
return zap.String(PeerServiceKey, name)
}
const (
ServiceTypeKey = "service_type"
// ServiceNameKey represents the NameKey of the service.
ServiceNameKey = "service_name"
// ServiceMethodKey represents the Method of the service.
ServiceMethodKey = "service_method"
// ServiceNamespaceKey represents a namespace for `service.name`. This needs to
// have meaning that helps to distinguish a group of services. For example, the
// team name that owns a group of services. `service.name` is expected to be
// unique within the same namespace.
ServiceNamespaceKey = "service_namespace"
// ServiceInstanceIDKey represents a unique identifier of the service instance. In conjunction
// with the `service.name` and `service.namespace` this must be unique.
ServiceInstanceIDKey = "service_instance.id"
// ServiceVersionKey represents the version of the service API.
ServiceVersionKey = "service_version"
)
// Deprecated: use semconv messaging attributes instead.
func FServiceType(name string) zap.Field {
return zap.String(ServiceTypeKey, name)
}
// Deprecated: use semconv messaging attributes instead.
func FServiceName(name string) zap.Field {
return zap.String(ServiceNameKey, name)
}
// Deprecated: use semconv messaging attributes instead.
func FServiceNamespace(namespace string) zap.Field {
return zap.String(ServiceNamespaceKey, namespace)
}
// Deprecated: use semconv messaging attributes instead.
func FServiceInstanceID(id string) zap.Field {
return zap.String(ServiceInstanceIDKey, id)
}
// Deprecated: use semconv messaging attributes instead.
func FServiceVersion(version string) zap.Field {
return zap.String(ServiceVersionKey, version)
}
// Deprecated: use semconv messaging attributes instead.
func FServiceMethod(method string) zap.Field {
return zap.String(ServiceMethodKey, method)
}

View File

@ -5,14 +5,18 @@ import (
)
const (
StreamQueueKey = "queue"
// Deprecated: use semconv messaging attributes instead.
StreamQueueKey = "queue"
// Deprecated: use semconv messaging attributes instead.
StreamSubjectKey = "subject"
)
// Deprecated: use semconv messaging attributes instead.
func FStreamQueue(queue string) zap.Field {
return zap.String(StreamQueueKey, queue)
}
// Deprecated: use semconv messaging attributes instead.
func FStreamSubject(name string) zap.Field {
return zap.String(StreamSubjectKey, name)
}

View File

@ -2,6 +2,7 @@ package log
import (
"fmt"
"strings"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.uber.org/zap"
@ -41,8 +42,8 @@ func NewLogger(level, encoding string) *zap.Logger {
config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
}
config.EncoderConfig.CallerKey = string(semconv.CodeFilePathKey)
config.EncoderConfig.StacktraceKey = string(semconv.CodeStacktraceKey)
config.EncoderConfig.CallerKey = strings.ReplaceAll(string(semconv.CodeFilePathKey), ".", "_")
config.EncoderConfig.StacktraceKey = strings.ReplaceAll(string(semconv.CodeStacktraceKey), ".", "_")
config.EncoderConfig.EncodeCaller = func(caller zapcore.EntryCaller, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString(caller.File)
}

48
log/otel.go Normal file
View File

@ -0,0 +1,48 @@
package log
import (
"strings"
"go.opentelemetry.io/otel/attribute"
"go.uber.org/zap"
)
func Attributes(attrs ...attribute.KeyValue) []zap.Field {
ret := make([]zap.Field, len(attrs))
for i, attr := range attrs {
ret[i] = Attribute(attr)
}
return ret
}
func Attribute(attr attribute.KeyValue) zap.Field {
switch attr.Value.Type() {
case attribute.BOOL:
return zap.Bool(AttributeKey(attr.Key), attr.Value.AsBool())
case attribute.BOOLSLICE:
return zap.Bools(AttributeKey(attr.Key), attr.Value.AsBoolSlice())
case attribute.INT64:
return zap.Int64(AttributeKey(attr.Key), attr.Value.AsInt64())
case attribute.INT64SLICE:
return zap.Int64s(AttributeKey(attr.Key), attr.Value.AsInt64Slice())
case attribute.FLOAT64:
return zap.Float64(AttributeKey(attr.Key), attr.Value.AsFloat64())
case attribute.FLOAT64SLICE:
return zap.Float64s(AttributeKey(attr.Key), attr.Value.AsFloat64Slice())
case attribute.STRING:
return zap.String(AttributeKey(attr.Key), attr.Value.AsString())
case attribute.STRINGSLICE:
if value := attr.Value.AsStringSlice(); len(value) == 1 {
return zap.String(AttributeKey(attr.Key), value[0])
} else {
return zap.Strings(AttributeKey(attr.Key), value)
}
default:
return zap.Any(AttributeKey(attr.Key), attr.Value.AsInterface())
}
}
func AttributeKey(key attribute.Key) string {
return strings.ReplaceAll(string(key), ".", "_")
}

View File

@ -7,7 +7,9 @@ import (
"net/http"
"strings"
keelsemconv "github.com/foomo/keel/semconv"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
@ -40,7 +42,7 @@ func WithError(l *zap.Logger, err error) *zap.Logger {
}
func WithServiceName(l *zap.Logger, name string) *zap.Logger {
return With(l, FServiceName(name))
return With(l, Attribute(semconv.ServiceName(name)))
}
func WithTraceID(l *zap.Logger, ctx context.Context) *zap.Logger {
@ -56,29 +58,22 @@ func WithHTTPServerName(l *zap.Logger, name string) *zap.Logger {
}
func WithHTTPFlavor(l *zap.Logger, r *http.Request) *zap.Logger {
switch r.ProtoMajor {
case 1:
return With(l, FHTTPFlavor(fmt.Sprintf("1.%d", r.ProtoMinor)))
case 2:
return With(l, FHTTPFlavor("2"))
default:
return l
}
return With(l, Attributes(semconv.NetworkProtocolName("HTTP"), semconv.NetworkProtocolVersion(fmt.Sprintf("%d.%d", r.ProtoMajor, r.ProtoMinor)))...)
}
func WithHTTPScheme(l *zap.Logger, r *http.Request) *zap.Logger {
if r.TLS != nil {
return With(l, FHTTPScheme("https"))
return With(l, Attribute(semconv.URLScheme("https")))
} else {
return With(l, FHTTPScheme("http"))
return With(l, Attribute(semconv.URLScheme("http")))
}
}
func WithHTTPSessionID(l *zap.Logger, r *http.Request) *zap.Logger {
if id := r.Header.Get("X-Session-Id"); id != "" {
return With(l, FHTTPSessionID(id))
return With(l, Attribute(semconv.SessionID(id)))
} else if id, ok := keelhttpcontext.GetSessionID(r.Context()); ok && id != "" {
return With(l, FHTTPSessionID(id))
return With(l, Attribute(semconv.SessionID(id)))
} else {
return l
}
@ -86,9 +81,9 @@ func WithHTTPSessionID(l *zap.Logger, r *http.Request) *zap.Logger {
func WithHTTPRequestID(l *zap.Logger, r *http.Request) *zap.Logger {
if id := r.Header.Get("X-Request-Id"); id != "" {
return With(l, FHTTPRequestID(id))
return With(l, Attribute(keelsemconv.HTTPXRequestID(id)))
} else if id, ok := keelhttpcontext.GetRequestID(r.Context()); ok && id != "" {
return With(l, FHTTPRequestID(id))
return With(l, Attribute(keelsemconv.HTTPXRequestID(id)))
} else {
return l
}
@ -96,9 +91,9 @@ func WithHTTPRequestID(l *zap.Logger, r *http.Request) *zap.Logger {
func WithHTTPReferer(l *zap.Logger, r *http.Request) *zap.Logger {
if value := r.Header.Get("X-Referer"); value != "" {
return With(l, FHTTPReferer(value))
return With(l, Attribute(keelsemconv.HTTPXRequestReferer(value)))
} else if value := r.Referer(); value != "" {
return With(l, FHTTPReferer(value))
return With(l, Attribute(keelsemconv.HTTPXRequestReferer(value)))
} else {
return l
}
@ -106,19 +101,19 @@ func WithHTTPReferer(l *zap.Logger, r *http.Request) *zap.Logger {
func WithHTTPHost(l *zap.Logger, r *http.Request) *zap.Logger {
if value := r.Header.Get("X-Forwarded-Host"); value != "" {
return With(l, FHTTPHost(value))
return With(l, Attribute(semconv.HostName(value)))
} else if !r.URL.IsAbs() {
return With(l, FHTTPHost(r.Host))
return With(l, Attribute(semconv.HostName(r.Host)))
} else {
return With(l, FHTTPHost(r.URL.Host))
return With(l, Attribute(semconv.HostName(r.URL.Host)))
}
}
func WithHTTPTrackingID(l *zap.Logger, r *http.Request) *zap.Logger {
if id := r.Header.Get("X-Tracking-Id"); id != "" {
return With(l, FHTTPTrackingID(id))
return With(l, Attribute(keelsemconv.TrackingID(id)))
} else if id, ok := keelhttpcontext.GetTrackingID(r.Context()); ok && id != "" {
return With(l, FHTTPTrackingID(id))
return With(l, Attribute(keelsemconv.TrackingID(id)))
} else {
return l
}
@ -142,7 +137,7 @@ func WithHTTPClientIP(l *zap.Logger, r *http.Request) *zap.Logger {
}
if clientIP != "" {
return With(l, FHTTPClientIP(clientIP))
return With(l, Attribute(semconv.ClientAddress(clientIP)))
}
return l
@ -159,12 +154,12 @@ func WithHTTPRequest(l *zap.Logger, r *http.Request) *zap.Logger {
l = WithHTTPClientIP(l, r)
l = WithTraceID(l, r.Context())
return With(l,
FHTTPMethod(r.Method),
FHTTPTarget(r.RequestURI),
FHTTPUserAgent(r.UserAgent()),
FHTTPRequestContentLength(r.ContentLength),
)
return With(l, Attributes(
semconv.URLPath(r.URL.Path),
semconv.UserAgentName(r.UserAgent()),
semconv.HTTPRequestMethodKey.String(r.Method),
semconv.HTTPRequestSizeKey.Int64(r.ContentLength),
)...)
}
func WithHTTPRequestOut(l *zap.Logger, r *http.Request) *zap.Logger {
@ -176,9 +171,10 @@ func WithHTTPRequestOut(l *zap.Logger, r *http.Request) *zap.Logger {
l = WithHTTPFlavor(l, r)
l = WithTraceID(l, r.Context())
return With(l,
FHTTPMethod(r.Method),
FHTTPTarget(r.URL.Path),
FHTTPWroteBytes(r.ContentLength),
)
return With(l, Attributes(
semconv.URLPath(r.URL.Path),
semconv.UserAgentName(r.UserAgent()),
semconv.HTTPRequestMethodKey.String(r.Method),
semconv.HTTPRequestSizeKey.Int64(r.ContentLength),
)...)
}

View File

@ -5,6 +5,7 @@ import (
"time"
httplog "github.com/foomo/keel/net/http/log"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
@ -98,8 +99,8 @@ func LoggerWithOptions(opts LoggerOptions) Middleware {
l = l.With(
log.FDuration(time.Since(start)),
log.FHTTPStatusCode(wr.StatusCode()),
log.FHTTPWroteBytes(int64(wr.Size())),
log.Attribute(semconv.HTTPResponseStatusCode(wr.StatusCode())),
log.Attribute(semconv.HTTPResponseSize(wr.Size())),
)
if labeler != nil {

View File

@ -4,6 +4,7 @@ import (
"net/http"
"time"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
@ -86,10 +87,10 @@ func Logger(opts ...LoggerOption) RoundTripware {
if err != nil {
l = log.WithError(l, err)
} else if resp != nil {
l = log.With(l,
log.FHTTPStatusCode(resp.StatusCode),
log.FHTTPRequestContentLength(resp.ContentLength),
)
l = log.With(l, log.Attributes(
semconv.HTTPResponseStatusCode(resp.StatusCode),
semconv.HTTPResponseBodySizeKey.Int64(resp.ContentLength),
)...)
statusCode = resp.StatusCode
}

View File

@ -7,6 +7,7 @@ import (
"github.com/nats-io/nats.go"
"github.com/pkg/errors"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.uber.org/zap"
"github.com/foomo/keel/log"
@ -122,7 +123,7 @@ func SubscriberWithUnmarshal(unmarshal UnmarshalFn) SubscriberOption {
func New(l *zap.Logger, name, addr string, opts ...Option) (*Stream, error) {
stream := &Stream{
l: l.With(
log.FMessagingSystem("jetstream"),
log.Attribute(semconv.MessagingSystemKey.String("jetstream")),
log.FName(name),
),
name: name,
@ -294,7 +295,11 @@ func (s *Stream) connect() error {
func (s *Stream) initNatsOptions() {
natsOpts := append([]nats.Option{
nats.ErrorHandler(func(conn *nats.Conn, subscription *nats.Subscription, err error) {
s.l.Error("nats error", log.FError(err), log.FStreamQueue(subscription.Queue), log.FStreamSubject(subscription.Subject))
s.l.Error("nats error",
log.FError(err),
log.Attribute(semconv.MessagingDestinationName(subscription.Queue)),
log.Attribute(semconv.MessagingDestinationSubscriptionName(subscription.Subject)),
)
}),
nats.ClosedHandler(func(conn *nats.Conn) {
if err := conn.LastError(); err != nil {

22
semconv/http.go Normal file
View File

@ -0,0 +1,22 @@
package semconv
import (
"go.opentelemetry.io/otel/attribute"
)
const (
// HTTPXRequestIDKey is the key for http.request.id.
HTTPXRequestIDKey = attribute.Key("http.request.id")
// HTTPXRequestRefererKey is the key for http.request.referer.
HTTPXRequestRefererKey = attribute.Key("http.request.referer")
)
// HTTPXRequestID returns a new attribute.KeyValue for http.request.id.
func HTTPXRequestID(v string) attribute.KeyValue {
return HTTPXRequestIDKey.String(v)
}
// HTTPXRequestReferer returns a new attribute.KeyValue for http.request.referer.
func HTTPXRequestReferer(v string) attribute.KeyValue {
return HTTPXRequestRefererKey.String(v)
}

29
semconv/keel.go Normal file
View File

@ -0,0 +1,29 @@
package semconv
import (
"go.opentelemetry.io/otel/attribute"
)
const (
// KeelServiceTypeKey is the key for keel.service.type.
KeelServiceTypeKey = attribute.Key("keel.service.type")
// KeelServiceNameKey is the key for keel.service.name.
KeelServiceNameKey = attribute.Key("keel.service.name")
// KeelServiceInstKey is the key for keel.service.inst.
KeelServiceInstKey = attribute.Key("keel.service.inst")
)
// KeelServiceType returns a new attribute.KeyValue for keel.service.type.
func KeelServiceType(v string) attribute.KeyValue {
return KeelServiceTypeKey.String(v)
}
// KeelServiceName returns a new attribute.KeyValue for keel.service.name.
func KeelServiceName(v string) attribute.KeyValue {
return KeelServiceNameKey.String(v)
}
// KeelServiceInst returns a new attribute.KeyValue for keel.service.inst.
func KeelServiceInst(v int) attribute.KeyValue {
return KeelServiceInstKey.Int(v)
}

15
semconv/tracking.go Normal file
View File

@ -0,0 +1,15 @@
package semconv
import (
"go.opentelemetry.io/otel/attribute"
)
const (
// TrackingIDKey is the key for tracking.id.
TrackingIDKey = attribute.Key("tracking.id")
)
// TrackingID returns a new attribute.KeyValue for tracking.id.
func TrackingID(v string) attribute.KeyValue {
return TrackingIDKey.String(v)
}

View File

@ -6,6 +6,7 @@ import (
"sync"
"sync/atomic"
"github.com/foomo/keel/semconv"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
@ -34,8 +35,8 @@ func NewGoRoutine(l *zap.Logger, name string, handler GoRoutineFn, opts ...GoRou
}
// enrich the log
l = log.WithAttributes(l,
log.KeelServiceTypeKey.String("goroutine"),
log.KeelServiceNameKey.String(name),
semconv.KeelServiceType("goroutine"),
semconv.KeelServiceType(name),
)
inst := &GoRoutine{
@ -92,7 +93,7 @@ func (s *GoRoutine) Start(ctx context.Context) error {
s.cancelLock.Unlock()
for i := range s.parallel {
l := log.WithAttributes(s.l, log.KeelServiceInstKey.Int(i))
l := log.WithAttributes(s.l, semconv.KeelServiceInst(i))
s.wg.Go(func() error {
return s.handler(ctx, l)
})

View File

@ -9,7 +9,9 @@ import (
"sync/atomic"
"time"
keelsemconv "github.com/foomo/keel/semconv"
"github.com/pkg/errors"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.uber.org/zap"
"github.com/foomo/keel/log"
@ -34,8 +36,8 @@ func NewHTTP(l *zap.Logger, name, addr string, handler http.Handler, middlewares
}
// enrich the log
l = log.WithAttributes(l,
log.KeelServiceTypeKey.String("http"),
log.KeelServiceNameKey.String(name),
keelsemconv.KeelServiceType("http"),
keelsemconv.KeelServiceName(name),
)
return &HTTP{
@ -87,7 +89,7 @@ func (s *HTTP) Start(ctx context.Context) error {
ip = "0.0.0.0"
}
fields = append(fields, log.FNetHostIP(ip), log.FNetHostPort(port))
fields = append(fields, log.Attributes(semconv.ServerAddress(ip), semconv.ServerPortKey.String(port))...)
}
s.l.Info("starting keel service", fields...)

View File

@ -8,6 +8,7 @@ import (
"github.com/foomo/keel/healthz"
"github.com/foomo/keel/interfaces"
"github.com/foomo/keel/log"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.uber.org/zap"
)
@ -30,7 +31,7 @@ func NewHealthz(l *zap.Logger, name, addr, path string, probes map[healthz.Type]
unavailable := func(l *zap.Logger, w http.ResponseWriter, r *http.Request, err error) {
if err != nil {
log.WithError(l, err).With(log.FHTTPTarget(r.RequestURI)).Debug("healthz probe failed")
log.WithError(l, err).With(log.Attribute(semconv.URLFull(r.RequestURI))).Debug("healthz probe failed")
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
}
}

View File

@ -16,6 +16,10 @@ type Context struct {
context.Context
}
func Ctx(ctx context.Context) Context {
return Context{ctx}
}
func (c Context) Log() *zap.Logger {
return Log(c.Context)
}
@ -54,7 +58,3 @@ func (c Context) Profile(handler func(ctx Context), labels ...string) {
handler(Ctx(ctx))
})
}
func Ctx(ctx context.Context) Context {
return Context{ctx}
}

View File

@ -22,7 +22,7 @@ func NewResource(ctx context.Context) (*resource.Resource, error) {
semconv.VCSRefHeadNameKey: {"GIT_BRANCH", "OTEL_VCS_HEAD_NAME"},
semconv.VCSRefHeadRevisionKey: {"GIT_COMMIT", "GIT_COMMIT_HASH", "OTEL_VCS_HEAD_REVSION"},
semconv.VCSRefHeadTypeKey: {"GIT_TYPE", "OTEL_VCS_HEAD_TYPE"},
"vcs_root_path": {"OTEL_VCS_ROOT_PATH"},
"vcs_root_path": {"REPO_PATH", "REPOSITORY_PATH", "GIT_REPOSITORY_PATH", "OTEL_VCS_ROOT_PATH"},
}
for k, keys := range envs {
for _, key := range keys {

View File

@ -5,6 +5,7 @@ import (
httplog "github.com/foomo/keel/net/http/log"
"github.com/pkg/errors"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.uber.org/zap"
"github.com/foomo/keel/log"
@ -48,7 +49,7 @@ func ServerError(l *zap.Logger, w http.ResponseWriter, r *http.Request, code int
} else {
l = log.WithError(l, err)
l = log.WithHTTPRequest(l, r)
l.Error("http server error", log.FHTTPStatusCode(code))
l.Error("http server error", log.Attribute(semconv.HTTPResponseStatusCode(code)))
}
// w.Header().Set(keelhttp.HeaderXError, err.Error()) TODO make configurable with better value
http.Error(w, http.StatusText(code), code)