257 lines
7.9 KiB
Go
257 lines
7.9 KiB
Go
package attr
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"runtime"
|
|
"runtime/debug"
|
|
|
|
"git.ma-al.com/maal-libraries/observer/pkg/level"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/semconv/v1.25.0"
|
|
"go.opentelemetry.io/otel/trace"
|
|
)
|
|
|
|
type KeyValue = attribute.KeyValue
|
|
|
|
type IntoTraceAttribute interface {
|
|
IntoTraceAttribute() attribute.KeyValue
|
|
}
|
|
|
|
type IntoTraceAttributes interface {
|
|
IntoTraceAttributes() []attribute.KeyValue
|
|
}
|
|
|
|
func CollectAttributes(attrs ...interface{}) []attribute.KeyValue {
|
|
collected := make([]attribute.KeyValue, len(attrs))
|
|
for _, a := range attrs {
|
|
switch a.(type) {
|
|
case []attribute.KeyValue:
|
|
collected = append(collected, a.([]attribute.KeyValue)...)
|
|
case attribute.KeyValue:
|
|
collected = append(collected, a.(attribute.KeyValue))
|
|
case IntoTraceAttribute:
|
|
collected = append(collected, a.(IntoTraceAttribute).IntoTraceAttribute())
|
|
case IntoTraceAttributes:
|
|
collected = append(collected, a.(IntoTraceAttributes).IntoTraceAttributes()...)
|
|
}
|
|
}
|
|
return collected
|
|
}
|
|
|
|
func WithAttributes(attrs ...interface{}) trace.SpanStartEventOption {
|
|
return trace.WithAttributes(CollectAttributes(attrs...)...)
|
|
}
|
|
|
|
const (
|
|
SeverityLevelKey = attribute.Key("level")
|
|
LogMessageLongKey = attribute.Key("log_message.long")
|
|
LogMessageShortKey = attribute.Key("log_message.short")
|
|
EnduserResponseMessageKey = attribute.Key("enduser.response_message")
|
|
SessionLanguageIdKey = attribute.Key("session.language_id")
|
|
SessionCountryIdKey = attribute.Key("session.country_id")
|
|
SessionCurrencyIdKey = attribute.Key("session.currency_id")
|
|
ProcessThreadsAvailableKey = attribute.Key("process.threads_available")
|
|
ServiceLayerKey = attribute.Key("service.layer")
|
|
ServiceLayerNameKey = attribute.Key("service.layer_name")
|
|
)
|
|
|
|
type ServiceArchitectureLayer string
|
|
|
|
const (
|
|
LayerFrameworkMiddleware ServiceArchitectureLayer = "framework_middleware"
|
|
LayerHandler = "handler"
|
|
LayerService = "service"
|
|
LayerRepository = "repository"
|
|
LayerORM = "orm"
|
|
LayerUtil = "util"
|
|
)
|
|
|
|
// Build an attribute with a value formatted as json
|
|
func JsonAttr(key string, jsonEl map[string]interface{}) attribute.KeyValue {
|
|
jsonStr, _ := json.Marshal(jsonEl)
|
|
return attribute.KeyValue{Key: attribute.Key(key), Value: attribute.StringValue(string(jsonStr))}
|
|
|
|
}
|
|
|
|
// An attribute informing about the severity or importance of an event using our own standard of log levels that
|
|
// can map to syslog level.
|
|
func SeverityLevel(lvl level.SeverityLevel) attribute.KeyValue {
|
|
return attribute.String(string(SeverityLevelKey), lvl.String())
|
|
}
|
|
|
|
func LogMessage(short string, expanded string) []attribute.KeyValue {
|
|
attrs := make([]attribute.KeyValue, 2)
|
|
attrs = append(attrs, LogMessageShort(short), LogMessageLong(expanded))
|
|
return attrs
|
|
}
|
|
|
|
// An attribute which value could be used as the full message field within the GELF format.
|
|
func LogMessageLong(msg string) attribute.KeyValue {
|
|
return attribute.String(string(LogMessageLongKey), msg)
|
|
}
|
|
|
|
// An attribute which value could be used as the short message field within the GELF format.
|
|
func LogMessageShort(msg string) attribute.KeyValue {
|
|
return attribute.String(string(LogMessageShortKey), msg)
|
|
}
|
|
|
|
// A message provided to the end user. For example, in case of API server errors it might be desired
|
|
// to provide to the user a message that does not leak too many details instead of sending an original
|
|
// (for a given package) error message.
|
|
func EnduserResponseMessage(msg string) attribute.KeyValue {
|
|
return attribute.String(string(EnduserResponseMessageKey), msg)
|
|
}
|
|
|
|
// Inspect the call stack to retrieve the information about a call site location including
|
|
// function name, file path, and line number.
|
|
func SourceCodeLocation(skipLevelsInCallStack int) []attribute.KeyValue {
|
|
pc, file, line, _ := runtime.Caller(1 + skipLevelsInCallStack)
|
|
funcName := runtime.FuncForPC(pc).Name()
|
|
|
|
return []attribute.KeyValue{
|
|
{
|
|
Key: semconv.CodeFunctionKey,
|
|
Value: attribute.StringValue(funcName),
|
|
},
|
|
{
|
|
Key: semconv.CodeFilepathKey,
|
|
Value: attribute.StringValue(file),
|
|
},
|
|
{
|
|
Key: semconv.CodeLineNumberKey,
|
|
Value: attribute.IntValue(line),
|
|
},
|
|
}
|
|
}
|
|
|
|
// Use within some panic handler to generate an attribute that will contain a stack trace.
|
|
func PanicStackTrace() attribute.KeyValue {
|
|
stackTrace := string(debug.Stack())
|
|
return semconv.ExceptionStacktrace(stackTrace)
|
|
}
|
|
|
|
// Builds attributes describing a server.
|
|
func Server(address string, port int) []attribute.KeyValue {
|
|
return []attribute.KeyValue{
|
|
{
|
|
Key: semconv.ServerAddressKey,
|
|
Value: attribute.StringValue(address),
|
|
},
|
|
{
|
|
Key: semconv.ServerPortKey,
|
|
Value: attribute.IntValue(port),
|
|
},
|
|
}
|
|
}
|
|
|
|
// Investigates the running process to derive attributes that describe it. This will only
|
|
// try to retrive these details which provide any valuable information at the start of a
|
|
// process.
|
|
func ProcessStart() []attribute.KeyValue {
|
|
attrs := make([]attribute.KeyValue, 5)
|
|
executablePath, err := os.Executable()
|
|
if err == nil {
|
|
attrs = append(attrs, semconv.ProcessExecutablePath(executablePath))
|
|
}
|
|
hostname, err := os.Hostname()
|
|
if err == nil {
|
|
attrs = append(attrs, semconv.HostName(hostname))
|
|
}
|
|
|
|
runtimeVersion := runtime.Version()
|
|
cpuThreads := runtime.NumCPU()
|
|
pid := os.Getpid()
|
|
attrs = append(attrs, semconv.ProcessParentPID(pid), semconv.ProcessRuntimeVersion(runtimeVersion), attribute.KeyValue{
|
|
Key: ProcessThreadsAvailableKey,
|
|
Value: attribute.IntValue(cpuThreads),
|
|
})
|
|
|
|
return attrs
|
|
}
|
|
|
|
// Id of an end user's session.
|
|
func SessionId(id string) attribute.KeyValue {
|
|
return attribute.KeyValue{
|
|
Key: semconv.SessionIDKey,
|
|
Value: attribute.StringValue(id),
|
|
}
|
|
}
|
|
|
|
// Id of a language associated with a user's session.
|
|
func SessionLanguageId(id uint) attribute.KeyValue {
|
|
return attribute.KeyValue{
|
|
Key: SessionLanguageIdKey,
|
|
Value: attribute.IntValue(int(id)),
|
|
}
|
|
}
|
|
|
|
// Id of a country associated with a user's session.
|
|
func SessionCountryId(id uint) attribute.KeyValue {
|
|
return attribute.KeyValue{
|
|
Key: SessionCountryIdKey,
|
|
Value: attribute.IntValue(int(id)),
|
|
}
|
|
}
|
|
|
|
// Id of a currency associated with a user's session.
|
|
func SessionCurrencyId(id uint) attribute.KeyValue {
|
|
return attribute.KeyValue{
|
|
Key: SessionCurrencyIdKey,
|
|
Value: attribute.IntValue(int(id)),
|
|
}
|
|
}
|
|
|
|
// Render details about session as attributes.
|
|
func Session(deets SessionDetails) []attribute.KeyValue {
|
|
return deets.IntoTraceAttributes()
|
|
}
|
|
|
|
// A collection of attributes that we at maal frequently attach to user sessions that can
|
|
// be converted into a collection of trace attributes. All fields are optional.
|
|
type SessionDetails struct {
|
|
ID *string
|
|
PreviousID *string
|
|
LanguageID *uint
|
|
CountryID *uint
|
|
CurrencyID *uint
|
|
}
|
|
|
|
func (deets SessionDetails) IntoTraceAttributes() []attribute.KeyValue {
|
|
attrs := make([]attribute.KeyValue, 4) // most frequently we won't have previous session ID
|
|
if deets.ID != nil {
|
|
attrs = append(attrs, SessionId(*deets.ID))
|
|
}
|
|
if deets.PreviousID != nil {
|
|
attrs = append(attrs, attribute.KeyValue{
|
|
Key: semconv.SessionPreviousIDKey,
|
|
Value: attribute.StringValue(*deets.PreviousID),
|
|
})
|
|
}
|
|
if deets.LanguageID != nil {
|
|
attrs = append(attrs, SessionLanguageId(*deets.LanguageID))
|
|
}
|
|
if deets.CountryID != nil {
|
|
attrs = append(attrs, SessionCountryId(*deets.CountryID))
|
|
}
|
|
if deets.CurrencyID != nil {
|
|
attrs = append(attrs, SessionCurrencyId(*deets.CurrencyID))
|
|
}
|
|
return attrs
|
|
}
|
|
|
|
// Describes a layer of a web server architecture with some of terms frequently used at maal.
|
|
func ServiceLayer(layer ServiceArchitectureLayer) attribute.KeyValue {
|
|
return attribute.KeyValue{
|
|
Key: ServiceLayerKey,
|
|
Value: attribute.StringValue(string(layer)),
|
|
}
|
|
}
|
|
|
|
func ServiceLayerName(name string) attribute.KeyValue {
|
|
return attribute.KeyValue{
|
|
Key: ServiceLayerNameKey,
|
|
Value: attribute.StringValue(name),
|
|
}
|
|
}
|