Documentation
¶
Index ¶
- func DefaultWriterClientConfig() writerClientConfig
- func GetLogger() otellog.Logger
- func GetMeter() otelmetric.Meter
- func GetTracer() oteltrace.Tracer
- func OtelAttr(key string, value any) otellog.KeyValue
- func SetClient(client *Client)
- func SetGlobalOtelProviders()
- type Attributes
- type Client
- type Config
- type Emitter
- type Message
- type Metadata
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefaultWriterClientConfig ¶
func DefaultWriterClientConfig() writerClientConfig
func GetMeter ¶
func GetMeter() otelmetric.Meter
func SetGlobalOtelProviders ¶
func SetGlobalOtelProviders()
Sets global OTel logger, tracer, meter providers from Client. Makes them accessible from anywhere in the code via global otel getters. Any package that relies on go.opentelemetry.io will be able to pick up configured global providers e.g [otelgrpc](https://pkg.go.dev/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc#example-NewServerHandler)
Types ¶
type Attributes ¶
type Client ¶
type Client struct {
Config Config
// Logger
Logger otellog.Logger
// Tracer
Tracer oteltrace.Tracer
// Meter
Meter otelmetric.Meter
// Message Emitter
Emitter Emitter
// Providers
LoggerProvider otellog.LoggerProvider
TracerProvider oteltrace.TracerProvider
MeterProvider otelmetric.MeterProvider
MessageLoggerProvider otellog.LoggerProvider
// OnClose
OnClose func() error
}
func GetClient ¶
func GetClient() *Client
Returns the global Beholder Client Its thread-safe and can be used concurrently
func NewClient ¶
NewClient creates a new Client with initialized OpenTelemetry components To handle OpenTelemetry errors use otel.SetErrorHandler(https://pkg.go.dev/go.opentelemetry.io/otel#SetErrorHandler)
Example ¶
package main
import (
"context"
"fmt"
"log"
"go.opentelemetry.io/otel"
"google.golang.org/protobuf/proto"
"github.com/smartcontractkit/chainlink-common/pkg/beholder"
"github.com/smartcontractkit/chainlink-common/pkg/beholder/pb"
)
func main() {
config := beholder.DefaultConfig()
// Initialize beholder otel client which sets up OTel components
client, err := beholder.NewClient(config)
if err != nil {
log.Fatalf("Error creating Beholder client: %v", err)
}
// Handle OTel errors
otel.SetErrorHandler(otelErrPrinter)
// Set global client so it will be accessible from anywhere through beholder functions
beholder.SetClient(client)
// Define a custom protobuf payload to emit
payload := &pb.TestCustomMessage{
BoolVal: true,
IntVal: 42,
FloatVal: 3.14,
StringVal: "Hello, World!",
}
payloadBytes, err := proto.Marshal(payload)
if err != nil {
log.Fatalf("Failed to marshal protobuf")
}
// Emit the custom message anywhere from application logic
fmt.Println("Emit custom messages")
for range 10 {
err := beholder.GetEmitter().Emit(context.Background(), payloadBytes,
"beholder_data_schema", "/custom-message/versions/1", // required
"beholder_data_type", "custom_message",
"foo", "bar",
)
if err != nil {
log.Printf("Error emitting message: %v", err)
}
}
}
var otelErrPrinter = otel.ErrorHandlerFunc(func(err error) {
log.Printf("otel error: %v", err)
})
Output: Emit custom messages
func NewNoopClient ¶
func NewNoopClient() *Client
Default client to fallback when is is not initialized properly
Example ¶
package main
import (
"context"
"fmt"
"log"
"github.com/smartcontractkit/chainlink-common/pkg/beholder"
)
func main() {
fmt.Println("Beholder is not initialized. Fall back to Noop OTel Client")
fmt.Println("Emitting custom message via noop otel client")
err := beholder.GetEmitter().Emit(context.Background(), []byte("test message"),
"beholder_data_schema", "/custom-message/versions/1", // required
)
if err != nil {
log.Printf("Error emitting message: %v", err)
}
}
Output: Beholder is not initialized. Fall back to Noop OTel Client Emitting custom message via noop otel client
func NewStdoutClient ¶
NewStdoutClient creates a new Client with exporters which send telemetry data to standard output Used for testing and debugging
func NewWriterClient ¶
NewWriterClient creates a new Client with otel exporters which send telemetry data to custom io.Writer
func (Client) ForPackage ¶
Returns a new Client with the same configuration but with a different package name
type Config ¶
type Config struct {
InsecureConnection bool
CACertFile string
OtelExporterGRPCEndpoint string
// OTel Resource
ResourceAttributes []otelattr.KeyValue
// Message Emitter
EmitterExportTimeout time.Duration
// Batch processing is enabled by default
// Disable it only for testing
EmitterBatchProcessor bool
// OTel Trace
TraceSampleRatio float64
TraceBatchTimeout time.Duration
TraceSpanExporter sdktrace.SpanExporter // optional additional exporter
// OTel Metric
MetricReaderInterval time.Duration
// OTel Log
LogExportTimeout time.Duration
// Batch processing is enabled by default
// Disable it only for testing
LogBatchProcessor bool
}
Example ¶
package main
import (
"fmt"
"time"
otelattr "go.opentelemetry.io/otel/attribute"
"github.com/smartcontractkit/chainlink-common/pkg/beholder"
)
const (
packageName = "beholder"
)
func main() {
config := beholder.Config{
InsecureConnection: true,
CACertFile: "",
OtelExporterGRPCEndpoint: "localhost:4317",
// Resource
ResourceAttributes: []otelattr.KeyValue{
otelattr.String("package_name", packageName),
otelattr.String("sender", "beholderclient"),
},
// Message Emitter
EmitterExportTimeout: 1 * time.Second,
EmitterBatchProcessor: true,
// Trace
TraceSampleRatio: 1,
TraceBatchTimeout: 1 * time.Second,
// Metric
MetricReaderInterval: 1 * time.Second,
// Log
LogExportTimeout: 1 * time.Second,
LogBatchProcessor: true,
}
fmt.Printf("%+v", config)
}
Output: {InsecureConnection:true CACertFile: OtelExporterGRPCEndpoint:localhost:4317 ResourceAttributes:[{Key:package_name Value:{vtype:4 numeric:0 stringly:beholder slice:<nil>}} {Key:sender Value:{vtype:4 numeric:0 stringly:beholderclient slice:<nil>}}] EmitterExportTimeout:1s EmitterBatchProcessor:true TraceSampleRatio:1 TraceBatchTimeout:1s TraceSpanExporter:<nil> MetricReaderInterval:1s LogExportTimeout:1s LogBatchProcessor:true}
func DefaultConfig ¶
func DefaultConfig() Config
func TestDefaultConfig ¶
func TestDefaultConfig() Config
type Emitter ¶
type Emitter interface {
// Sends message with bytes and attributes to OTel Collector
Emit(ctx context.Context, body []byte, attrKVs ...any) error
}
func GetEmitter ¶
func GetEmitter() Emitter
type Message ¶
type Message struct {
Attrs Attributes
Body []byte
}
Example ¶
package main
import (
"fmt"
"github.com/smartcontractkit/chainlink-common/pkg/beholder"
)
func main() {
// Create message with body and attributes
m1 := beholder.NewMessage([]byte{1}, beholder.Attributes{"key_string": "value"})
fmt.Println("#1", m1)
// Create attributes
additionalAttributes := beholder.Attributes{
"key_string": "new value",
"key_int32": int32(1),
}
// Add attributes to message
m1.AddAttributes(additionalAttributes)
fmt.Println("#2", m1)
// Create mmpty message struct
m2 := beholder.Message{}
fmt.Println("#3", m2)
// Add attributes to message
m2.AddAttributes(beholder.Attributes{"key_int": 1})
fmt.Println("#4", m2)
// Update attribute key_int
m2.AddAttributes(beholder.Attributes{"key_int": 2})
fmt.Println("#5", m2)
// Set message body
m2.Body = []byte("0123")
fmt.Println("#6", m2)
// Reset attributes
m2.Attrs = beholder.Attributes{}
fmt.Println("#7", m2)
// Reset body
m2.Body = nil
fmt.Println("#8", m2)
// Shalow copy of message
m3 := beholder.NewMessage(m1.Body, m1.Attrs)
fmt.Println("#9", m3)
m1.Body[0] = byte(2) // Wil mutate m3
fmt.Println("#10", m3)
// Deep copy
m4 := m1.Copy()
fmt.Println("#11", m4)
m1.Body[0] = byte(3) // Should not mutate m4
fmt.Println("#12", m4)
// Create message with mixed attributes: kv pairs and maps
m5 := beholder.NewMessage([]byte{1},
// Add attributes from the map
map[string]any{
"key1": "value1",
},
// Add attributes from KV pair
"key2", "value2",
// Add attributes from Attributes map
beholder.Attributes{"key3": "value3"},
// Add attributes from KV pair
"key4", "value4",
// Modify key1
"key1", "value5",
// Modify key2
map[string]any{
"key2": "value6",
},
)
fmt.Println("#13", m5)
// Create message with no attributes
m6 := beholder.NewMessage([]byte{1}, beholder.Attributes{})
// Add attributes using AddAttributes
m6.AddAttributes(
"key1", "value1",
"key2", "value2",
)
fmt.Println("#14", m6)
}
Output: #1 Message{Attrs: map[key_string:value], Body: [1]} #2 Message{Attrs: map[key_int32:1 key_string:new value], Body: [1]} #3 Message{Attrs: map[], Body: []} #4 Message{Attrs: map[key_int:1], Body: []} #5 Message{Attrs: map[key_int:2], Body: []} #6 Message{Attrs: map[key_int:2], Body: [48 49 50 51]} #7 Message{Attrs: map[], Body: [48 49 50 51]} #8 Message{Attrs: map[], Body: []} #9 Message{Attrs: map[key_int32:1 key_string:new value], Body: [1]} #10 Message{Attrs: map[key_int32:1 key_string:new value], Body: [2]} #11 Message{Attrs: map[key_int32:1 key_string:new value], Body: [2]} #12 Message{Attrs: map[key_int32:1 key_string:new value], Body: [2]} #13 Message{Attrs: map[key1:value5 key2:value6 key3:value3 key4:value4], Body: [1]} #14 Message{Attrs: map[key1:value1 key2:value2], Body: [1]}
func NewMessage ¶
func (*Message) AddAttributes ¶
func (*Message) OtelRecord ¶
type Metadata ¶
type Metadata struct {
// REQUIRED FIELDS
// Schema Registry URI to fetch schema
BeholderDataSchema string `validate:"required,uri"`
// OPTIONAL FIELDS
// The version of the CL node.
NodeVersion string
// mTLS public key for the node operator. This is used as an identity key but with the added benefit of being able to provide signatures.
NodeCsaKey string
// Signature from CSA private key.
NodeCsaSignature string
DonID string
// The RDD network name the CL node is operating with.
NetworkName []string
WorkflowID string
WorkflowName string
WorkflowOwnerAddress string
// Hash of the workflow spec.
WorkflowSpecID string
// The unique execution of a workflow.
WorkflowExecutionID string
// The address for the contract.
CapabilityContractAddress string
CapabilityID string
CapabilityVersion string
CapabilityName string
NetworkChainID string
}
Example ¶
package main
import (
"fmt"
"github.com/smartcontractkit/chainlink-common/pkg/beholder"
)
func testMetadata() beholder.Metadata {
return beholder.Metadata{
NodeVersion: "v1.0.0",
NodeCsaKey: "test_key",
NodeCsaSignature: "test_signature",
DonID: "test_don_id",
NetworkName: []string{"test_network"},
WorkflowID: "test_workflow_id",
WorkflowName: "test_workflow_name",
WorkflowOwnerAddress: "test_owner_address",
WorkflowSpecID: "test_spec_id",
WorkflowExecutionID: "test_execution_id",
BeholderDataSchema: "/schemas/ids/test_schema",
CapabilityContractAddress: "test_contract_address",
CapabilityID: "test_capability_id",
CapabilityVersion: "test_capability_version",
CapabilityName: "test_capability_name",
NetworkChainID: "test_chain_id",
}
}
func main() {
m := testMetadata()
fmt.Printf("%#v\n", m)
fmt.Println(m.Attributes())
}
Output: beholder.Metadata{BeholderDataSchema:"/schemas/ids/test_schema", NodeVersion:"v1.0.0", NodeCsaKey:"test_key", NodeCsaSignature:"test_signature", DonID:"test_don_id", NetworkName:[]string{"test_network"}, WorkflowID:"test_workflow_id", WorkflowName:"test_workflow_name", WorkflowOwnerAddress:"test_owner_address", WorkflowSpecID:"test_spec_id", WorkflowExecutionID:"test_execution_id", CapabilityContractAddress:"test_contract_address", CapabilityID:"test_capability_id", CapabilityVersion:"test_capability_version", CapabilityName:"test_capability_name", NetworkChainID:"test_chain_id"} map[beholder_data_schema:/schemas/ids/test_schema capability_contract_address:test_contract_address capability_id:test_capability_id capability_name:test_capability_name capability_version:test_capability_version don_id:test_don_id network_chain_id:test_chain_id network_name:[test_network] node_csa_key:test_key node_csa_signature:test_signature node_version:v1.0.0 workflow_execution_id:test_execution_id workflow_id:test_workflow_id workflow_name:test_workflow_name workflow_owner_address:test_owner_address workflow_spec_id:test_spec_id]
func NewMetadata ¶
func NewMetadata(attrs Attributes) *Metadata
func (Metadata) Attributes ¶
func (m Metadata) Attributes() Attributes
func (*Metadata) FromAttributes ¶
func (m *Metadata) FromAttributes(attrs Attributes) *Metadata
Sets metadata fields from attributes