app

package
v0.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 15, 2024 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package app provides helpers for common bedrock.App implementation patterns.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Recover

func Recover(app bedrock.App) bedrock.App

Recover will wrap the give bedrock.App with panic recovery. If the recovered panic value implements [error] then it will be directly returned. If it does not implement [error] then a [PanicError] will be returned instead.

Example
app := runFunc(func(ctx context.Context) error {
	panic("hello world")
	return nil
})

err := Recover(app).Run(context.Background())

var perr bedrock.PanicError
if !errors.As(err, &perr) {
	fmt.Println("should be a panic error.")
	return
}

fmt.Println(perr.Value)
Output:

hello world
Example (ErrorValue)
app := runFunc(func(ctx context.Context) error {
	panic(errors.New("hello world"))
	return nil
})

err := Recover(app).Run(context.Background())

var perr bedrock.PanicError
if !errors.As(err, &perr) {
	fmt.Println("should be a panic error.")
	return
}

fmt.Println(perr.Unwrap())
Output:

hello world

func WithLifecycleHooks

func WithLifecycleHooks(app bedrock.App, lifecycle Lifecycle) bedrock.App

WithLifecycleHooks wraps a given bedrock.App in an implementation that runs [LifecycleHook]s around the execution of app.Run.

Example
var app bedrock.App = runFunc(func(ctx context.Context) error {
	return nil
})

postRun := LifecycleHookFunc(func(ctx context.Context) error {
	fmt.Println("ran post run hook")
	return nil
})

app = WithLifecycleHooks(app, Lifecycle{
	PostRun: postRun,
})

err := app.Run(context.Background())
if err != nil {
	fmt.Println(err)
	return
}
Output:

ran post run hook
Example (UnrecoveredPanic)
var app bedrock.App = runFunc(func(ctx context.Context) error {
	panic("hello world")
	return nil
})

postRun := LifecycleHookFunc(func(ctx context.Context) error {
	fmt.Println("ran post run hook")
	return nil
})

app = WithLifecycleHooks(app, Lifecycle{
	PostRun: postRun,
})

run := func(ctx context.Context) error {
	// recover here so the panic doesn't crash the example
	defer func() {
		recover()
	}()

	return app.Run(ctx)
}

err := run(context.Background())
if err != nil {
	fmt.Println(err)
	return
}
Output:

ran post run hook

func WithOTel

func WithOTel(app bedrock.App, opts ...OTelOption) bedrock.App

WithOTel

Example (LoggerProvider)
var app bedrock.App = runFunc(func(ctx context.Context) error {
	// here we're using the otelslog bridge which will use the global
	// LoggerProvider for us to create a otel Logger and map between
	// the slog and otel log record types.
	logger := otelslog.NewLogger("app")
	logger.InfoContext(ctx, "hello")
	return nil
})

var lp *sdklog.LoggerProvider
var buf bytes.Buffer
app = WithOTel(
	app,
	OTelLoggerProvider(func(ctx context.Context) (log.LoggerProvider, error) {
		// NOTE: this is only for example purposes. DO NOT USE IN PRODUCTION!!!
		exp, err := stdoutlog.New(
			stdoutlog.WithWriter(&buf),
		)
		if err != nil {
			return nil, err
		}

		p := sdklog.NewSimpleProcessor(exp)

		lp = sdklog.NewLoggerProvider(
			sdklog.WithProcessor(p),
		)
		return lp, nil
	}),
)

err := app.Run(context.Background())
if err != nil {
	fmt.Println(err)
	return
}

// Ensure that the app log is flushed to buf
err = lp.Shutdown(context.Background())
if err != nil {
	fmt.Println(err)
	return
}

b, err := io.ReadAll(&buf)
if err != nil {
	fmt.Println(err)
	return
}

var m struct {
	Body struct {
		Value string `json:"Value"`
	} `json:"Body"`
	Scope struct {
		Name string `json:"Name"`
	} `json:"Scope"`
}
err = json.Unmarshal(b, &m)
if err != nil {
	fmt.Println(err)
	return
}

fmt.Println(m.Scope.Name, m.Body.Value)
Output:

app hello
Example (MeterProvider)
var app bedrock.App = runFunc(func(ctx context.Context) error {
	counter, err := otel.Meter("app").Int64Counter("Run")
	if err != nil {
		return err
	}
	counter.Add(ctx, 1)
	return nil
})

var mp *sdkmetric.MeterProvider
var buf bytes.Buffer
app = WithOTel(
	app,
	OTelMeterProvider(func(ctx context.Context) (metric.MeterProvider, error) {
		// NOTE: this is only for example purposes. DO NOT USE IN PRODUCTION!!!
		exp, err := stdoutmetric.New(
			stdoutmetric.WithWriter(&buf),
		)
		if err != nil {
			return nil, err
		}

		r := sdkmetric.NewPeriodicReader(exp)

		mp = sdkmetric.NewMeterProvider(
			sdkmetric.WithReader(r),
		)
		return mp, nil
	}),
)

err := app.Run(context.Background())
if err != nil {
	fmt.Println(err)
	return
}

// Ensure that the app metric is flushed to buf
err = mp.Shutdown(context.Background())
if err != nil {
	fmt.Println(err)
	return
}

b, err := io.ReadAll(&buf)
if err != nil {
	fmt.Println(err)
	return
}

var m struct {
	ScopeMetrics []struct {
		Metrics []struct {
			Name string `json:"Name"`
			Data struct {
				DataPoints []struct {
					Value int `json:"Value"`
				} `json:"DataPoints"`
			} `json:"Data"`
		} `json:"Metrics"`
	} `json:"ScopeMetrics"`
}
err = json.Unmarshal(b, &m)
if err != nil {
	fmt.Println(err)
	return
}

metric := m.ScopeMetrics[0].Metrics[0]
fmt.Println(metric.Name, metric.Data.DataPoints[0].Value)
Output:

Run 1
Example (TextMapPropogator)
carrier := make(propagation.MapCarrier)
var app bedrock.App = runFunc(func(ctx context.Context) error {
	tmp := otel.GetTextMapPropagator()
	tmp.Inject(ctx, carrier)
	return nil
})

app = WithOTel(
	app,
	OTelTextMapPropogator(func(ctx context.Context) (propagation.TextMapPropagator, error) {
		tmp := propagation.Baggage{}
		return tmp, nil
	}),
)

m, _ := baggage.NewMember("hello", "world")
b, _ := baggage.New(m)
ctx := baggage.ContextWithBaggage(context.Background(), b)

err := app.Run(ctx)
if err != nil {
	fmt.Println(err)
	return
}

ctx = propagation.Baggage{}.Extract(context.Background(), carrier)
b = baggage.FromContext(ctx)
m = b.Member("hello")
fmt.Println(m.Value())
Output:

world
Example (TracerProvider)
var app bedrock.App = runFunc(func(ctx context.Context) error {
	_, span := otel.Tracer("app").Start(ctx, "Run")
	defer span.End()
	return nil
})

var tp *sdktrace.TracerProvider
var buf bytes.Buffer
app = WithOTel(
	app,
	OTelTracerProvider(func(ctx context.Context) (trace.TracerProvider, error) {
		// NOTE: this is only for example purposes. DO NOT USE IN PRODUCTION!!!
		exp, err := stdouttrace.New(
			stdouttrace.WithWriter(&buf),
		)
		if err != nil {
			return nil, err
		}

		sp := sdktrace.NewSimpleSpanProcessor(exp)

		tp = sdktrace.NewTracerProvider(
			sdktrace.WithSpanProcessor(sp),
		)
		return tp, nil
	}),
)

err := app.Run(context.Background())
if err != nil {
	fmt.Println(err)
	return
}

// Ensure that the app trace is flushed to buf
err = tp.Shutdown(context.Background())
if err != nil {
	fmt.Println(err)
	return
}

b, err := io.ReadAll(&buf)
if err != nil {
	fmt.Println(err)
	return
}

var m map[string]any
err = json.Unmarshal(b, &m)
if err != nil {
	fmt.Println(err)
	return
}

fmt.Println(m["Name"])
Output:

Run

func WithSignalNotifications

func WithSignalNotifications(app bedrock.App, signals ...os.Signal) bedrock.App

WithSignalNotifications wraps a given bedrock.App in an implementation that cancels the context.Context that's passed to app.Run if an os.Signal is received by the running process.

Types

type Lifecycle

type Lifecycle struct {
	// PostRun is always executed regardless if the underlying [bedrock.App]
	// returns an error or panics.
	PostRun LifecycleHook
}

Lifecycle

type LifecycleHook

type LifecycleHook interface {
	Run(context.Context) error
}

LifecycleHook represents functionality that needs to be performed at a specific "time" relative to the execution of bedrock.App.Run.

func ComposeLifecycleHooks added in v0.16.0

func ComposeLifecycleHooks(hooks ...LifecycleHook) LifecycleHook

ComposeLifecycleHooks combines multiple [LifecycleHook]s into a single hook. Each hook is called sequentially and each hook is called irregardless if a previous hook returned an error or not. Any and all errors are then returned after all hooks have been ran.

Example
var app bedrock.App = runFunc(func(ctx context.Context) error {
	return nil
})

app = WithLifecycleHooks(app, Lifecycle{
	PostRun: ComposeLifecycleHooks(
		LifecycleHookFunc(func(ctx context.Context) error {
			fmt.Println("one")
			return nil
		}),
		LifecycleHookFunc(func(ctx context.Context) error {
			fmt.Println("two")
			return nil
		}),
		LifecycleHookFunc(func(ctx context.Context) error {
			fmt.Println("three")
			return nil
		}),
	),
})

err := app.Run(context.Background())
if err != nil {
	fmt.Println(err)
	return
}
Output:

one
two
three

type LifecycleHookFunc

type LifecycleHookFunc func(context.Context) error

LifecycleHookFunc is a convenient helper type for implementing a LifecycleHook from just a regular func.

func (LifecycleHookFunc) Run

Run implements the LifecycleHook interface.

type OTelOption

type OTelOption func(*otelOptions)

OTelOption

func OTelLoggerProvider

func OTelLoggerProvider(f func(context.Context) (log.LoggerProvider, error)) OTelOption

OTelLoggerProvider

func OTelMeterProvider

func OTelMeterProvider(f func(context.Context) (metric.MeterProvider, error)) OTelOption

OTelMeterProvider

func OTelTextMapPropogator

func OTelTextMapPropogator(f func(context.Context) (propagation.TextMapPropagator, error)) OTelOption

OTelTextMapPropogator

func OTelTracerProvider

func OTelTracerProvider(f func(context.Context) (trace.TracerProvider, error)) OTelOption

OTelTracerProvider

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL