-
Notifications
You must be signed in to change notification settings - Fork 257
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Log (JSON) formatting hook; span export
Added formatting for logrus export hook to marshal structs as JSON strings, as well as format other known types (eg, time.Time). Updated span export to: * include span kind * include status code * log if span attributes were dropped Added helper `log.Format*` functions to format Time and other structs to JSON. This is already done by the logging hook, but allows spans format their attributes consistently, since they must be converted to strings before being exported. Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>
- Loading branch information
Showing
14 changed files
with
756 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package hcserror | ||
|
||
import "errors" | ||
|
||
// IsAny returns true if errors.Is is true for any of the provided errors, errs. | ||
func IsAny(err error, errs ...error) bool { | ||
for _, e := range errs { | ||
if errors.Is(err, e) { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package log | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net" | ||
"reflect" | ||
"time" | ||
|
||
"github.com/containerd/containerd/log" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
const TimeFormat = log.RFC3339NanoFixed | ||
|
||
func FormatTime(t time.Time) string { | ||
return t.Format(TimeFormat) | ||
} | ||
|
||
// FormatIO formats net.Conn and other types that have an `Addr()` or `Name()`. | ||
// | ||
// See FormatEnabled for more information. | ||
func FormatIO(ctx context.Context, v interface{}) string { | ||
m := make(map[string]string) | ||
m["type"] = reflect.TypeOf(v).String() | ||
|
||
switch t := v.(type) { | ||
case net.Conn: | ||
m["local_address"] = formatAddr(t.LocalAddr()) | ||
m["remote_address"] = formatAddr(t.RemoteAddr()) | ||
case interface{ Addr() net.Addr }: | ||
m["address"] = formatAddr(t.Addr()) | ||
default: | ||
return Format(ctx, t) | ||
} | ||
|
||
return Format(ctx, m) | ||
} | ||
|
||
func formatAddr(a net.Addr) string { | ||
return a.Network() + "://" + a.String() | ||
} | ||
|
||
// Format formats an object into a JSON string, without any indendtation or | ||
// HTML escapes. | ||
// Context is used to output a log waring if the conversion fails. | ||
// | ||
// This is intended primarily for `trace.StringAttribute()` | ||
func Format(ctx context.Context, v interface{}) string { | ||
b, err := encode(v) | ||
if err != nil { | ||
G(ctx).WithError(err).Warning("could not format value") | ||
return "" | ||
} | ||
|
||
return string(b) | ||
} | ||
|
||
func encode(v interface{}) ([]byte, error) { | ||
return encodeBuffer(&bytes.Buffer{}, v) | ||
} | ||
|
||
func encodeBuffer(buf *bytes.Buffer, v interface{}) ([]byte, error) { | ||
enc := json.NewEncoder(buf) | ||
enc.SetEscapeHTML(false) | ||
enc.SetIndent("", "") | ||
|
||
if err := enc.Encode(v); err != nil { | ||
err = fmt.Errorf("could not marshall %T to JSON for logging: %w", v, err) | ||
return nil, err | ||
} | ||
|
||
// encoder.Encode appends a newline to the end | ||
return bytes.TrimSpace(buf.Bytes()), nil | ||
} | ||
|
||
// GetCallerName checks if the entry appears caller caller information and returns the function name. | ||
// | ||
// This is intended to be used with "github.com/Microsoft/go-winio/pkg/etwlogrus".WithGetName. | ||
func GetCallerName(e *logrus.Entry) string { | ||
if e.Caller == nil { | ||
return "" | ||
} | ||
if e.Caller.Func != nil { | ||
return e.Caller.Func.Name() | ||
} | ||
return e.Caller.Function | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package oc | ||
|
||
import ( | ||
"io" | ||
"net" | ||
"os" | ||
|
||
"github.com/containerd/containerd/errdefs" | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/status" | ||
|
||
"github.com/Microsoft/hcsshim/internal/hcserror" | ||
) | ||
|
||
// todo: break import cycle with "internal/hcs" and errors errors defined there | ||
// todo: add errors defined in "internal/guest/gcserror" (Hresult does not implement error) | ||
|
||
func toStatusCode(err error) codes.Code { | ||
// checks if err implements GRPCStatus() *"google.golang.org/grpc/status".Status, | ||
// wraps an error defined in "github.com/containerd/containerd/errdefs", or is a | ||
// context timeout or cancelled error | ||
if s, ok := status.FromError(errdefs.ToGRPC(err)); ok { | ||
return s.Code() | ||
} | ||
|
||
switch { | ||
// case hcserror.IsAny(err): | ||
// return codes.Cancelled | ||
case hcserror.IsAny(err, os.ErrInvalid): | ||
return codes.InvalidArgument | ||
case hcserror.IsAny(err, os.ErrDeadlineExceeded): | ||
return codes.DeadlineExceeded | ||
case hcserror.IsAny(err, os.ErrNotExist): | ||
return codes.NotFound | ||
case hcserror.IsAny(err, os.ErrExist): | ||
return codes.AlreadyExists | ||
case hcserror.IsAny(err, os.ErrPermission): | ||
return codes.PermissionDenied | ||
// case hcserror.IsAny(err): | ||
// return codes.ResourceExhausted | ||
case hcserror.IsAny(err, os.ErrClosed, net.ErrClosed, io.ErrClosedPipe, io.ErrShortBuffer): | ||
return codes.FailedPrecondition | ||
// case hcserror.IsAny(err): | ||
// return codes.Aborted | ||
// case hcserror.IsAny(err): | ||
// return codes.OutOfRange | ||
// case hcserror.IsAny(err): | ||
// return codes.Unimplemented | ||
case hcserror.IsAny(err, io.ErrNoProgress): | ||
return codes.Internal | ||
// case hcserror.IsAny(err): | ||
// return codes.Unavailable | ||
case hcserror.IsAny(err, io.ErrShortWrite, io.ErrUnexpectedEOF): | ||
return codes.DataLoss | ||
// case hcserror.IsAny(err): | ||
// return codes.Unauthenticated | ||
default: | ||
return codes.Unknown | ||
} | ||
} |
Oops, something went wrong.