From 6a24b595f0de17d2c217fc3af4ac82e46fa93b1f Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 18 Mar 2021 10:39:23 +0800 Subject: [PATCH] upgrade otel from 0.16.0 to 0.18.0 --- database/gdb/gdb_core_tracing.go | 24 ++++----- database/gredis/gredis_conn_tracing.go | 14 +++--- go.mod | 4 +- net/ghttp/ghttp_middleware_tracing.go | 50 +++++++++---------- net/ghttp/internal/client/client_tracing.go | 29 +++++------ .../internal/client/client_tracing_tracer.go | 45 ++++++++--------- net/gtrace/gtrace.go | 32 ++++++++---- net/gtrace/gtrace_baggage.go | 14 +++--- net/gtrace/gtrace_carrier.go | 42 +++++++++------- os/gfile/gfile.go | 8 ++- 10 files changed, 139 insertions(+), 123 deletions(-) diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index 678ad8558..3c746ec2c 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -14,8 +14,8 @@ import ( "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" ) @@ -59,33 +59,33 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { if sql.Error != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, sql.Error)) } - labels := make([]label.KeyValue, 0) + labels := make([]attribute.KeyValue, 0) labels = append(labels, gtrace.CommonLabels()...) labels = append(labels, - label.String(tracingAttrDbType, c.db.GetConfig().Type), + attribute.String(tracingAttrDbType, c.db.GetConfig().Type), ) if c.db.GetConfig().Host != "" { - labels = append(labels, label.String(tracingAttrDbHost, c.db.GetConfig().Host)) + labels = append(labels, attribute.String(tracingAttrDbHost, c.db.GetConfig().Host)) } if c.db.GetConfig().Port != "" { - labels = append(labels, label.String(tracingAttrDbPort, c.db.GetConfig().Port)) + labels = append(labels, attribute.String(tracingAttrDbPort, c.db.GetConfig().Port)) } if c.db.GetConfig().Name != "" { - labels = append(labels, label.String(tracingAttrDbName, c.db.GetConfig().Name)) + labels = append(labels, attribute.String(tracingAttrDbName, c.db.GetConfig().Name)) } if c.db.GetConfig().User != "" { - labels = append(labels, label.String(tracingAttrDbUser, c.db.GetConfig().User)) + labels = append(labels, attribute.String(tracingAttrDbUser, c.db.GetConfig().User)) } if filteredLinkInfo := c.db.FilteredLinkInfo(); filteredLinkInfo != "" { - labels = append(labels, label.String(tracingAttrDbLink, c.db.FilteredLinkInfo())) + labels = append(labels, attribute.String(tracingAttrDbLink, c.db.FilteredLinkInfo())) } if group := c.db.GetGroup(); group != "" { - labels = append(labels, label.String(tracingAttrDbGroup, group)) + labels = append(labels, attribute.String(tracingAttrDbGroup, group)) } span.SetAttributes(labels...) span.AddEvent(tracingEventDbExecution, trace.WithAttributes( - label.String(tracingEventDbExecutionSql, sql.Format), - label.String(tracingEventDbExecutionCost, fmt.Sprintf(`%d ms`, sql.End-sql.Start)), - label.String(tracingEventDbExecutionType, sql.Type), + attribute.String(tracingEventDbExecutionSql, sql.Format), + attribute.String(tracingEventDbExecutionCost, fmt.Sprintf(`%d ms`, sql.End-sql.Start)), + attribute.String(tracingEventDbExecutionType, sql.Type), )) } diff --git a/database/gredis/gredis_conn_tracing.go b/database/gredis/gredis_conn_tracing.go index 2e4c794fa..9ee411323 100644 --- a/database/gredis/gredis_conn_tracing.go +++ b/database/gredis/gredis_conn_tracing.go @@ -14,8 +14,8 @@ import ( "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" ) @@ -68,14 +68,14 @@ func (c *Conn) addTracingItem(item *tracingItem) { } span.SetAttributes(gtrace.CommonLabels()...) span.SetAttributes( - label.String(tracingAttrRedisHost, c.redis.config.Host), - label.Int(tracingAttrRedisPort, c.redis.config.Port), - label.Int(tracingAttrRedisDb, c.redis.config.Db), + attribute.String(tracingAttrRedisHost, c.redis.config.Host), + attribute.Int(tracingAttrRedisPort, c.redis.config.Port), + attribute.Int(tracingAttrRedisDb, c.redis.config.Db), ) jsonBytes, _ := json.Marshal(item.arguments) span.AddEvent(tracingEventRedisExecution, trace.WithAttributes( - label.String(tracingEventRedisExecutionCommand, item.commandName), - label.String(tracingEventRedisExecutionCost, fmt.Sprintf(`%d ms`, item.costMilli)), - label.String(tracingEventRedisExecutionArguments, string(jsonBytes)), + attribute.String(tracingEventRedisExecutionCommand, item.commandName), + attribute.String(tracingEventRedisExecutionCost, fmt.Sprintf(`%d ms`, item.costMilli)), + attribute.String(tracingEventRedisExecutionArguments, string(jsonBytes)), )) } diff --git a/go.mod b/go.mod index 6268b8843..0b8f1b44b 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,9 @@ require ( github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf github.com/mattn/go-runewidth v0.0.10 // indirect github.com/olekukonko/tablewriter v0.0.1 - go.opentelemetry.io/otel v0.16.0 + go.opentelemetry.io/otel v0.18.0 + go.opentelemetry.io/otel/oteltest v0.18.0 + go.opentelemetry.io/otel/trace v0.18.0 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index 22b4cdc2b..052b4ee7e 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -13,16 +13,17 @@ import ( "github.com/gogf/gf/net/ghttp/internal/client" "github.com/gogf/gf/net/ghttp/internal/httputil" "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/text/gstr" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" ) const ( - tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Server" tracingEventHttpRequest = "http.request" tracingEventHttpRequestHeaders = "http.request.headers" @@ -41,7 +42,7 @@ func MiddlewareClientTracing(c *Client, r *http.Request) (*ClientResponse, error // MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry. func MiddlewareServerTracing(r *Request) { tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) - ctx := otel.GetTextMapPropagator().Extract(r.Context(), r.Header) + ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)) ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindServer)) defer span.End() @@ -51,21 +52,17 @@ func MiddlewareServerTracing(r *Request) { r.SetCtx(ctx) // Request content logging. - var reqBodyContent string - if r.ContentLength <= tracingMaxContentLogSize { - reqBodyContentBytes, _ := ioutil.ReadAll(r.Body) - r.Body = utils.NewReadCloser(reqBodyContentBytes, false) - reqBodyContent = string(reqBodyContentBytes) - } else { - reqBodyContent = fmt.Sprintf( - "[Request Body Too Large For Tracing, Max: %d bytes]", - tracingMaxContentLogSize, - ) - } + reqBodyContentBytes, _ := ioutil.ReadAll(r.Body) + r.Body = utils.NewReadCloser(reqBodyContentBytes, false) + span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( - label.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)), - label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)), - label.String(tracingEventHttpRequestBody, reqBodyContent), + attribute.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)), + attribute.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)), + attribute.String(tracingEventHttpRequestBody, gstr.StrLimit( + string(reqBodyContentBytes), + gtrace.MaxContentLogSize(), + "...", + )), )) // Continue executing. @@ -77,17 +74,16 @@ func MiddlewareServerTracing(r *Request) { } // Response content logging. var resBodyContent string - if r.Response.BufferLength() <= tracingMaxContentLogSize { - resBodyContent = r.Response.BufferString() - } else { - resBodyContent = fmt.Sprintf( - "[Response Body Too Large For Tracing, Max: %d bytes]", - tracingMaxContentLogSize, - ) - } + resBodyContent = r.Response.BufferString() + resBodyContent = gstr.StrLimit( + r.Response.BufferString(), + gtrace.MaxContentLogSize(), + "...", + ) + span.AddEvent(tracingEventHttpResponse, trace.WithAttributes( - label.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())), - label.String(tracingEventHttpResponseBody, resBodyContent), + attribute.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())), + attribute.String(tracingEventHttpResponseBody, resBodyContent), )) return } diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index 244ea05b9..567ea6c14 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -12,9 +12,11 @@ import ( "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/net/ghttp/internal/httputil" "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/text/gstr" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" @@ -22,7 +24,6 @@ import ( ) const ( - tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Client" tracingAttrHttpAddressRemote = "http.address.remote" tracingAttrHttpAddressLocal = "http.address.local" @@ -48,7 +49,7 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro span.SetAttributes(gtrace.CommonLabels()...) // Inject tracing content into http header. - otel.GetTextMapPropagator().Inject(ctx, r.Header) + otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header)) // Continue client handler executing. response, err = c.Next( @@ -64,21 +65,17 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro if response == nil || response.Response == nil { return } - var resBodyContent string - if response.ContentLength <= tracingMaxContentLogSize { - reqBodyContentBytes, _ := ioutil.ReadAll(response.Body) - resBodyContent = string(reqBodyContentBytes) - response.Body = utils.NewReadCloser(reqBodyContentBytes, false) - } else { - resBodyContent = fmt.Sprintf( - "[Response Body Too Large For Tracing, Max: %d bytes]", - tracingMaxContentLogSize, - ) - } + + reqBodyContentBytes, _ := ioutil.ReadAll(response.Body) + response.Body = utils.NewReadCloser(reqBodyContentBytes, false) span.AddEvent(tracingEventHttpResponse, trace.WithAttributes( - label.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(response.Header)), - label.String(tracingEventHttpResponseBody, resBodyContent), + attribute.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(response.Header)), + attribute.String(tracingEventHttpResponseBody, gstr.StrLimit( + string(reqBodyContentBytes), + gtrace.MaxContentLogSize(), + "...", + )), )) return } diff --git a/net/ghttp/internal/client/client_tracing_tracer.go b/net/ghttp/internal/client/client_tracing_tracer.go index 694db4db2..ef4733f45 100644 --- a/net/ghttp/internal/client/client_tracing_tracer.go +++ b/net/ghttp/internal/client/client_tracing_tracer.go @@ -12,8 +12,9 @@ import ( "fmt" "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/text/gstr" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" @@ -39,11 +40,11 @@ func newClientTrace(ctx context.Context, span trace.Span, request *http.Request) request: request, headers: make(map[string]interface{}), } - if ct.request.ContentLength <= tracingMaxContentLogSize { - reqBodyContent, _ := ioutil.ReadAll(ct.request.Body) - ct.requestBody = reqBodyContent - ct.request.Body = utils.NewReadCloser(reqBodyContent, false) - } + + reqBodyContent, _ := ioutil.ReadAll(ct.request.Body) + ct.requestBody = reqBodyContent + ct.request.Body = utils.NewReadCloser(reqBodyContent, false) + return &httptrace.ClientTrace{ GetConn: ct.getConn, GotConn: ct.gotConn, @@ -70,8 +71,8 @@ func (ct *clientTracer) getConn(host string) { func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) { ct.span.SetAttributes( - label.String(tracingAttrHttpAddressRemote, info.Conn.RemoteAddr().String()), - label.String(tracingAttrHttpAddressLocal, info.Conn.LocalAddr().String()), + attribute.String(tracingAttrHttpAddressRemote, info.Conn.RemoteAddr().String()), + attribute.String(tracingAttrHttpAddressLocal, info.Conn.LocalAddr().String()), ) } @@ -83,7 +84,7 @@ func (ct *clientTracer) putIdleConn(err error) { func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) { ct.span.SetAttributes( - label.String(tracingAttrHttpDnsStart, info.Host), + attribute.String(tracingAttrHttpDnsStart, info.Host), ) } @@ -99,13 +100,13 @@ func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) { ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) } ct.span.SetAttributes( - label.String(tracingAttrHttpDnsDone, buffer.String()), + attribute.String(tracingAttrHttpDnsDone, buffer.String()), ) } func (ct *clientTracer) connectStart(network, addr string) { ct.span.SetAttributes( - label.String(tracingAttrHttpConnectStart, network+"@"+addr), + attribute.String(tracingAttrHttpConnectStart, network+"@"+addr), ) } @@ -114,7 +115,7 @@ func (ct *clientTracer) connectDone(network, addr string, err error) { ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) } ct.span.SetAttributes( - label.String(tracingAttrHttpConnectDone, network+"@"+addr), + attribute.String(tracingAttrHttpConnectDone, network+"@"+addr), ) } @@ -144,19 +145,15 @@ func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { if info.Err != nil { ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) } - var bodyContent string - if ct.request.ContentLength <= tracingMaxContentLogSize { - bodyContent = string(ct.requestBody) - } else { - bodyContent = fmt.Sprintf( - "[Request Body Too Large For Logging, Max: %d bytes]", - tracingMaxContentLogSize, - ) - } + ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( - label.Any(tracingEventHttpRequestHeaders, ct.headers), - label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context)), - label.String(tracingEventHttpRequestBody, bodyContent), + attribute.Any(tracingEventHttpRequestHeaders, ct.headers), + attribute.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context)), + attribute.String(tracingEventHttpRequestBody, gstr.StrLimit( + string(ct.requestBody), + gtrace.MaxContentLogSize(), + "...", + )), )) } diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index a166ddcb0..98490cd7f 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -9,11 +9,13 @@ package gtrace import ( "context" + "fmt" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/net/gipv4" + "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "os" @@ -23,12 +25,14 @@ import ( const ( tracingCommonKeyIpIntranet = `ip.intranet` tracingCommonKeyIpHostname = `hostname` + cmdEnvKey = "gf.gtrace" // Configuration key for command argument or environment. ) var ( - intranetIps, _ = gipv4.GetIntranetIpArray() - intranetIpStr = strings.Join(intranetIps, ",") - hostname, _ = os.Hostname() + intranetIps, _ = gipv4.GetIntranetIpArray() + intranetIpStr = strings.Join(intranetIps, ",") + hostname, _ = os.Hostname() + tracingMaxContentLogSize = 256 * 1024 // Max log size for request and response body, especially for HTTP/RPC request. // defaultTextMapPropagator is the default propagator for context propagation between peers. defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, @@ -37,15 +41,23 @@ var ( ) func init() { + if maxContentLogSize := gcmd.GetOptWithEnv(fmt.Sprintf("%s.maxlogsize", cmdEnvKey)).Int(); maxContentLogSize > 0 { + tracingMaxContentLogSize = maxContentLogSize + } CheckSetDefaultTextMapPropagator() } +// MaxContentLogSize returns the max log size for request and response body, especially for HTTP/RPC request. +func MaxContentLogSize() int { + return tracingMaxContentLogSize +} + // CommonLabels returns common used attribute labels: // ip.intranet, hostname. -func CommonLabels() []label.KeyValue { - return []label.KeyValue{ - label.String(tracingCommonKeyIpHostname, hostname), - label.String(tracingCommonKeyIpIntranet, intranetIpStr), +func CommonLabels() []attribute.KeyValue { + return []attribute.KeyValue{ + attribute.String(tracingCommonKeyIpHostname, hostname), + attribute.String(tracingCommonKeyIpIntranet, intranetIpStr), } } @@ -94,13 +106,13 @@ func GetSpanId(ctx context.Context) string { } // SetBaggageValue is a convenient function for adding one key-value pair to baggage. -// Note that it uses label.Any to set the key-value pair. +// Note that it uses attribute.Any to set the key-value pair. func SetBaggageValue(ctx context.Context, key string, value interface{}) context.Context { return NewBaggage(ctx).SetValue(key, value) } // SetBaggageMap is a convenient function for adding map key-value pairs to baggage. -// Note that it uses label.Any to set the key-value pair. +// Note that it uses attribute.Any to set the key-value pair. func SetBaggageMap(ctx context.Context, data map[string]interface{}) context.Context { return NewBaggage(ctx).SetMap(data) } diff --git a/net/gtrace/gtrace_baggage.go b/net/gtrace/gtrace_baggage.go index f06aaa9c7..28a175c20 100644 --- a/net/gtrace/gtrace_baggage.go +++ b/net/gtrace/gtrace_baggage.go @@ -10,8 +10,8 @@ import ( "context" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/baggage" - "go.opentelemetry.io/otel/label" ) // Baggage holds the data through all tracing spans. @@ -35,18 +35,18 @@ func (b *Baggage) Ctx() context.Context { } // SetValue is a convenient function for adding one key-value pair to baggage. -// Note that it uses label.Any to set the key-value pair. +// Note that it uses attribute.Any to set the key-value pair. func (b *Baggage) SetValue(key string, value interface{}) context.Context { - b.ctx = baggage.ContextWithValues(b.ctx, label.Any(key, value)) + b.ctx = baggage.ContextWithValues(b.ctx, attribute.Any(key, value)) return b.ctx } // SetMap is a convenient function for adding map key-value pairs to baggage. -// Note that it uses label.Any to set the key-value pair. +// Note that it uses attribute.Any to set the key-value pair. func (b *Baggage) SetMap(data map[string]interface{}) context.Context { - pairs := make([]label.KeyValue, 0) + pairs := make([]attribute.KeyValue, 0) for k, v := range data { - pairs = append(pairs, label.Any(k, v)) + pairs = append(pairs, attribute.Any(k, v)) } b.ctx = baggage.ContextWithValues(b.ctx, pairs...) return b.ctx @@ -70,6 +70,6 @@ func (b *Baggage) GetMap() *gmap.StrAnyMap { // GetVar retrieves value and returns a *gvar.Var for specified key from baggage. func (b *Baggage) GetVar(key string) *gvar.Var { - value := baggage.Value(b.ctx, label.Key(key)) + value := baggage.Value(b.ctx, attribute.Key(key)) return gvar.New(value.AsInterface()) } diff --git a/net/gtrace/gtrace_carrier.go b/net/gtrace/gtrace_carrier.go index 6cc55f857..c0cd41979 100644 --- a/net/gtrace/gtrace_carrier.go +++ b/net/gtrace/gtrace_carrier.go @@ -11,41 +11,49 @@ import ( "github.com/gogf/gf/util/gconv" ) -type Carrier struct { - data map[string]interface{} -} +// Carrier is the storage medium used by a TextMapPropagator. +type Carrier map[string]interface{} -func NewCarrier(data ...map[string]interface{}) *Carrier { - carrier := &Carrier{} +func NewCarrier(data ...map[string]interface{}) Carrier { if len(data) > 0 && data[0] != nil { - carrier.data = data[0] + return data[0] } else { - carrier.data = make(map[string]interface{}) + return make(map[string]interface{}) } - return carrier } -func (c *Carrier) Get(k string) string { - return gconv.String(c.data[k]) +// Get returns the value associated with the passed key. +func (c Carrier) Get(k string) string { + return gconv.String(c[k]) } -func (c *Carrier) Set(k, v string) { - c.data[k] = v +// Set stores the key-value pair. +func (c Carrier) Set(k, v string) { + c[k] = v } -func (c *Carrier) MustMarshal() []byte { - b, err := json.Marshal(c.data) +// Keys lists the keys stored in this carrier. +func (c Carrier) Keys() []string { + keys := make([]string, 0, len(c)) + for k := range c { + keys = append(keys, k) + } + return keys +} + +func (c Carrier) MustMarshal() []byte { + b, err := json.Marshal(c) if err != nil { panic(err) } return b } -func (c *Carrier) String() string { +func (c Carrier) String() string { return string(c.MustMarshal()) } -func (c *Carrier) UnmarshalJSON(b []byte) error { +func (c Carrier) UnmarshalJSON(b []byte) error { carrier := NewCarrier(nil) - return json.Unmarshal(b, carrier.data) + return json.Unmarshal(b, carrier) } diff --git a/os/gfile/gfile.go b/os/gfile/gfile.go index b21d20a43..84eede389 100644 --- a/os/gfile/gfile.go +++ b/os/gfile/gfile.go @@ -343,10 +343,14 @@ func Name(path string) string { // Dir returns all but the last element of path, typically the path's directory. // After dropping the final element, Dir calls Clean on the path and trailing // slashes are removed. -// If the path is empty, Dir returns ".". -// If the path consists entirely of separators, Dir returns a single separator. +// If the `path` is empty, Dir returns ".". +// If the `path` is ".", Dir treats the path as current working directory. +// If the `path` consists entirely of separators, Dir returns a single separator. // The returned path does not end in a separator unless it is the root directory. func Dir(path string) string { + if path == "." { + return filepath.Dir(RealPath(path)) + } return filepath.Dir(path) }