Giter Club home page Giter Club logo

clues's Introduction

CLUES

PkgGoDev goreportcard

A golang library for tracking runtime variables via ctx, passing them upstream within errors, and retrieving context- and error-bound variables for logging.

Aggregate runtime state in ctx

Track runtime variables by adding them to the context.

func foo(ctx context.Context, someID string) error {
    ctx = clues.Add(ctx, "importantID", someID)
    return bar(ctx, someID)
}

Keep error messages readable and augment your telemetry by packing errors with structured data.

func bar(ctx context.Context, someID string) error {
    ctx = clues.Add(ctx, "importantID", someID)
    err := errors.New("a bad happened")
    if err != nil {
        return clues.Stack(err).WithClues(ctx)
    }
    return nil
}

Retrive structured data from your errors for logging and other telemetry.

func main() {
    err := foo(context.Background(), "importantID")
    if err != nil {
        logger.
            Error("calling foo").
            WithError(err).
            WithAll(clues.InErr(err))
    }
}

Track individual process flows

Each clues addition traces its additions with a tree of IDs, chaining those traces into the "clues_trace" value. This lets you quickly and easily filter logs to a specific process tree.

func iterateOver(ctx context.Context, users []string) {
    // automatically adds "clues_trace":"id_a"
    ctx = clues.Add(ctx, "status", good)
    for i, user := range users {
        // automatically appends another id to "clues_trace": "id_a,id_n"
        ictx := clues.Add(ctx, "currentUser", user, "iter", i)
        err := doSomething(ictx, user)
        if err != nil {
            ictx = clues.Add(ictx, "status", bad)
        }
    }
}

Interoperable with pkg/errors

Clues errors can be wrapped by pkg/errors without slicing out any stored data.

func getIt(someID string) error {
    return clues.New("oh no!").With("importantID", someID)
}

func getItWrapper(someID string) error {
    if err := getIt(someID); err != nil {
        return errors.Wrap(err, "getting the thing")
    }

    return nil
}

func main() {
    err := getItWrapper("id")
    if err != nil {
        fmt.Println("error getting", err, "with vals", clues.InErr(err))
    }
}

Stackable errors

Error stacking lets you embed error sentinels without slicing out the current error's data or relying on err.Error() strings.

var ErrorCommonFailure = "a common failure condition"

func do() error {
    if err := dependency.Do(); err != nil {
        return clues.Stack(ErrorCommonFailure, err)
    }
    
    return nil
}

func main() {
    err := do()
    if errors.Is(err, ErrCommonFailure) {
        // true!
    }
}

Labeling Errors

Rather than build an errors.As-compliant local error to annotate downstream errors, labels allow you to categorize errors with expected qualities.

Augment downstream errors with labels

func foo(ctx context.Context, someID string) error {
    err := externalPkg.DoThing(ctx, someID)
    if err != nil {
        return clues.Wrap(err).Label("retryable")
    }
    return nil
}

Check your labels upstream.

func main() {
    err := foo(context.Background(), "importantID")
    if err != nil {
        if clues.HasLabel(err, "retryable")) {
            err := foo(context.Background(), "importantID")
        }
    }
}

Design

Clues is not the first of its kind: ctx-err-combo packages already exist. Most other packages tend to couple the two notions, packing both into a single handler. This is, in my opinion, an anti-pattern. Errors are not context, and context are not errors. Unifying the two can couple layers together, and your maintenance woes from handling that coupling are not worth the tradeoff in syntactical sugar.

In turn, Clues maintains a clear separation between accumulating data into a context and passing data back in an error. Both handlers operate independent of the other, so you can choose to only use the ctx (accumulate data into the context, but maybe log it instead of returning data in the err) or the err (only pack immedaite details into the error).

References

Similar Art

Fault is most similar in design to this package, and also attempts to maintain separation between errors and contexts. The differences are largely syntactical: Fault prefers a composable interface with decorator packages. I like to keep error production as terse as possible, thus preferring a more populated interface of methods over the decorator design.

References

clues's People

Contributors

ryanfkeepers avatar dependabot[bot] avatar ashmrtn avatar uncledru avatar vkamra avatar

Stargazers

Daniel Miranda avatar Abhishek Pandey avatar Niraj Tolia avatar  avatar Aarti Jivrajani avatar

Watchers

Nočnica Mellifera avatar  avatar  avatar Abhishek Pandey avatar Danny avatar  avatar  avatar

clues's Issues

`Hide()` hashes output even if input implements Concealer interface

Not really sure which way forward we want to take this, but there's a bit of oddness around how marshal and clues.Hide interact. marshal will conceal the output if the item being concealed implements the Concealer interface. This data is then stored in what I assume is meant to be the "string" representation of the item. Hide then calls Conceal on the (potentially already concealed) representation generated by marshal. This can cause nicely formatted but obfuscated information to lose useful data as it's reduced to a single hash

Open question: what is secret.str field meant to represent?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.