observer/pkg/attr/attr.go
Natalia Goc ab5b70704d standardize commonly used attributes
Some commonly used at maal attributes have been encoded as consts with
convinience wrappers similar to those of semconv package from otel sdk.
Additionally some utils that can generate mutliple attributes were added.
2024-05-16 13:45:13 +02:00

225 lines
6.9 KiB
Go

package attr
import (
"encoding/json"
"os"
"runtime"
"runtime/debug"
"git.ma-al.com/gora_filip/pkg/level"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/semconv/v1.25.0"
)
type IntoTraceAttribute interface {
IntoTraceAttribute() attribute.KeyValue
}
type IntoTraceAttributes interface {
IntoTraceAttributes() []attribute.KeyValue
}
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")
)
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)),
}
}