a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh
at main 17 kB view raw
1// Copyright 2022 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package slog 6 7import ( 8 "context" 9 "fmt" 10 "io" 11 "reflect" 12 "strconv" 13 "sync" 14 "time" 15 16 "golang.org/x/exp/slices" 17 "golang.org/x/exp/slog/internal/buffer" 18) 19 20// A Handler handles log records produced by a Logger.. 21// 22// A typical handler may print log records to standard error, 23// or write them to a file or database, or perhaps augment them 24// with additional attributes and pass them on to another handler. 25// 26// Any of the Handler's methods may be called concurrently with itself 27// or with other methods. It is the responsibility of the Handler to 28// manage this concurrency. 29// 30// Users of the slog package should not invoke Handler methods directly. 31// They should use the methods of [Logger] instead. 32type Handler interface { 33 // Enabled reports whether the handler handles records at the given level. 34 // The handler ignores records whose level is lower. 35 // It is called early, before any arguments are processed, 36 // to save effort if the log event should be discarded. 37 // If called from a Logger method, the first argument is the context 38 // passed to that method, or context.Background() if nil was passed 39 // or the method does not take a context. 40 // The context is passed so Enabled can use its values 41 // to make a decision. 42 Enabled(context.Context, Level) bool 43 44 // Handle handles the Record. 45 // It will only be called when Enabled returns true. 46 // The Context argument is as for Enabled. 47 // It is present solely to provide Handlers access to the context's values. 48 // Canceling the context should not affect record processing. 49 // (Among other things, log messages may be necessary to debug a 50 // cancellation-related problem.) 51 // 52 // Handle methods that produce output should observe the following rules: 53 // - If r.Time is the zero time, ignore the time. 54 // - If r.PC is zero, ignore it. 55 // - Attr's values should be resolved. 56 // - If an Attr's key and value are both the zero value, ignore the Attr. 57 // This can be tested with attr.Equal(Attr{}). 58 // - If a group's key is empty, inline the group's Attrs. 59 // - If a group has no Attrs (even if it has a non-empty key), 60 // ignore it. 61 Handle(context.Context, Record) error 62 63 // WithAttrs returns a new Handler whose attributes consist of 64 // both the receiver's attributes and the arguments. 65 // The Handler owns the slice: it may retain, modify or discard it. 66 WithAttrs(attrs []Attr) Handler 67 68 // WithGroup returns a new Handler with the given group appended to 69 // the receiver's existing groups. 70 // The keys of all subsequent attributes, whether added by With or in a 71 // Record, should be qualified by the sequence of group names. 72 // 73 // How this qualification happens is up to the Handler, so long as 74 // this Handler's attribute keys differ from those of another Handler 75 // with a different sequence of group names. 76 // 77 // A Handler should treat WithGroup as starting a Group of Attrs that ends 78 // at the end of the log event. That is, 79 // 80 // logger.WithGroup("s").LogAttrs(level, msg, slog.Int("a", 1), slog.Int("b", 2)) 81 // 82 // should behave like 83 // 84 // logger.LogAttrs(level, msg, slog.Group("s", slog.Int("a", 1), slog.Int("b", 2))) 85 // 86 // If the name is empty, WithGroup returns the receiver. 87 WithGroup(name string) Handler 88} 89 90type defaultHandler struct { 91 ch *commonHandler 92 // log.Output, except for testing 93 output func(calldepth int, message string) error 94} 95 96func newDefaultHandler(output func(int, string) error) *defaultHandler { 97 return &defaultHandler{ 98 ch: &commonHandler{json: false}, 99 output: output, 100 } 101} 102 103func (*defaultHandler) Enabled(_ context.Context, l Level) bool { 104 return l >= LevelInfo 105} 106 107// Collect the level, attributes and message in a string and 108// write it with the default log.Logger. 109// Let the log.Logger handle time and file/line. 110func (h *defaultHandler) Handle(ctx context.Context, r Record) error { 111 buf := buffer.New() 112 buf.WriteString(r.Level.String()) 113 buf.WriteByte(' ') 114 buf.WriteString(r.Message) 115 state := h.ch.newHandleState(buf, true, " ", nil) 116 defer state.free() 117 state.appendNonBuiltIns(r) 118 119 // skip [h.output, defaultHandler.Handle, handlerWriter.Write, log.Output] 120 return h.output(4, buf.String()) 121} 122 123func (h *defaultHandler) WithAttrs(as []Attr) Handler { 124 return &defaultHandler{h.ch.withAttrs(as), h.output} 125} 126 127func (h *defaultHandler) WithGroup(name string) Handler { 128 return &defaultHandler{h.ch.withGroup(name), h.output} 129} 130 131// HandlerOptions are options for a TextHandler or JSONHandler. 132// A zero HandlerOptions consists entirely of default values. 133type HandlerOptions struct { 134 // AddSource causes the handler to compute the source code position 135 // of the log statement and add a SourceKey attribute to the output. 136 AddSource bool 137 138 // Level reports the minimum record level that will be logged. 139 // The handler discards records with lower levels. 140 // If Level is nil, the handler assumes LevelInfo. 141 // The handler calls Level.Level for each record processed; 142 // to adjust the minimum level dynamically, use a LevelVar. 143 Level Leveler 144 145 // ReplaceAttr is called to rewrite each non-group attribute before it is logged. 146 // The attribute's value has been resolved (see [Value.Resolve]). 147 // If ReplaceAttr returns an Attr with Key == "", the attribute is discarded. 148 // 149 // The built-in attributes with keys "time", "level", "source", and "msg" 150 // are passed to this function, except that time is omitted 151 // if zero, and source is omitted if AddSource is false. 152 // 153 // The first argument is a list of currently open groups that contain the 154 // Attr. It must not be retained or modified. ReplaceAttr is never called 155 // for Group attributes, only their contents. For example, the attribute 156 // list 157 // 158 // Int("a", 1), Group("g", Int("b", 2)), Int("c", 3) 159 // 160 // results in consecutive calls to ReplaceAttr with the following arguments: 161 // 162 // nil, Int("a", 1) 163 // []string{"g"}, Int("b", 2) 164 // nil, Int("c", 3) 165 // 166 // ReplaceAttr can be used to change the default keys of the built-in 167 // attributes, convert types (for example, to replace a `time.Time` with the 168 // integer seconds since the Unix epoch), sanitize personal information, or 169 // remove attributes from the output. 170 ReplaceAttr func(groups []string, a Attr) Attr 171} 172 173// Keys for "built-in" attributes. 174const ( 175 // TimeKey is the key used by the built-in handlers for the time 176 // when the log method is called. The associated Value is a [time.Time]. 177 TimeKey = "time" 178 // LevelKey is the key used by the built-in handlers for the level 179 // of the log call. The associated value is a [Level]. 180 LevelKey = "level" 181 // MessageKey is the key used by the built-in handlers for the 182 // message of the log call. The associated value is a string. 183 MessageKey = "msg" 184 // SourceKey is the key used by the built-in handlers for the source file 185 // and line of the log call. The associated value is a string. 186 SourceKey = "source" 187) 188 189type commonHandler struct { 190 json bool // true => output JSON; false => output text 191 opts HandlerOptions 192 preformattedAttrs []byte 193 groupPrefix string // for text: prefix of groups opened in preformatting 194 groups []string // all groups started from WithGroup 195 nOpenGroups int // the number of groups opened in preformattedAttrs 196 mu sync.Mutex 197 w io.Writer 198} 199 200func (h *commonHandler) clone() *commonHandler { 201 // We can't use assignment because we can't copy the mutex. 202 return &commonHandler{ 203 json: h.json, 204 opts: h.opts, 205 preformattedAttrs: slices.Clip(h.preformattedAttrs), 206 groupPrefix: h.groupPrefix, 207 groups: slices.Clip(h.groups), 208 nOpenGroups: h.nOpenGroups, 209 w: h.w, 210 } 211} 212 213// enabled reports whether l is greater than or equal to the 214// minimum level. 215func (h *commonHandler) enabled(l Level) bool { 216 minLevel := LevelInfo 217 if h.opts.Level != nil { 218 minLevel = h.opts.Level.Level() 219 } 220 return l >= minLevel 221} 222 223func (h *commonHandler) withAttrs(as []Attr) *commonHandler { 224 h2 := h.clone() 225 // Pre-format the attributes as an optimization. 226 prefix := buffer.New() 227 defer prefix.Free() 228 prefix.WriteString(h.groupPrefix) 229 state := h2.newHandleState((*buffer.Buffer)(&h2.preformattedAttrs), false, "", prefix) 230 defer state.free() 231 if len(h2.preformattedAttrs) > 0 { 232 state.sep = h.attrSep() 233 } 234 state.openGroups() 235 for _, a := range as { 236 state.appendAttr(a) 237 } 238 // Remember the new prefix for later keys. 239 h2.groupPrefix = state.prefix.String() 240 // Remember how many opened groups are in preformattedAttrs, 241 // so we don't open them again when we handle a Record. 242 h2.nOpenGroups = len(h2.groups) 243 return h2 244} 245 246func (h *commonHandler) withGroup(name string) *commonHandler { 247 if name == "" { 248 return h 249 } 250 h2 := h.clone() 251 h2.groups = append(h2.groups, name) 252 return h2 253} 254 255func (h *commonHandler) handle(r Record) error { 256 state := h.newHandleState(buffer.New(), true, "", nil) 257 defer state.free() 258 if h.json { 259 state.buf.WriteByte('{') 260 } 261 // Built-in attributes. They are not in a group. 262 stateGroups := state.groups 263 state.groups = nil // So ReplaceAttrs sees no groups instead of the pre groups. 264 rep := h.opts.ReplaceAttr 265 // time 266 if !r.Time.IsZero() { 267 key := TimeKey 268 val := r.Time.Round(0) // strip monotonic to match Attr behavior 269 if rep == nil { 270 state.appendKey(key) 271 state.appendTime(val) 272 } else { 273 state.appendAttr(Time(key, val)) 274 } 275 } 276 // level 277 key := LevelKey 278 val := r.Level 279 if rep == nil { 280 state.appendKey(key) 281 state.appendString(val.String()) 282 } else { 283 state.appendAttr(Any(key, val)) 284 } 285 // source 286 if h.opts.AddSource { 287 state.appendAttr(Any(SourceKey, r.source())) 288 } 289 key = MessageKey 290 msg := r.Message 291 if rep == nil { 292 state.appendKey(key) 293 state.appendString(msg) 294 } else { 295 state.appendAttr(String(key, msg)) 296 } 297 state.groups = stateGroups // Restore groups passed to ReplaceAttrs. 298 state.appendNonBuiltIns(r) 299 state.buf.WriteByte('\n') 300 301 h.mu.Lock() 302 defer h.mu.Unlock() 303 _, err := h.w.Write(*state.buf) 304 return err 305} 306 307func (s *handleState) appendNonBuiltIns(r Record) { 308 // preformatted Attrs 309 if len(s.h.preformattedAttrs) > 0 { 310 s.buf.WriteString(s.sep) 311 s.buf.Write(s.h.preformattedAttrs) 312 s.sep = s.h.attrSep() 313 } 314 // Attrs in Record -- unlike the built-in ones, they are in groups started 315 // from WithGroup. 316 s.prefix = buffer.New() 317 defer s.prefix.Free() 318 s.prefix.WriteString(s.h.groupPrefix) 319 s.openGroups() 320 r.Attrs(func(a Attr) bool { 321 s.appendAttr(a) 322 return true 323 }) 324 if s.h.json { 325 // Close all open groups. 326 for range s.h.groups { 327 s.buf.WriteByte('}') 328 } 329 // Close the top-level object. 330 s.buf.WriteByte('}') 331 } 332} 333 334// attrSep returns the separator between attributes. 335func (h *commonHandler) attrSep() string { 336 if h.json { 337 return "," 338 } 339 return " " 340} 341 342// handleState holds state for a single call to commonHandler.handle. 343// The initial value of sep determines whether to emit a separator 344// before the next key, after which it stays true. 345type handleState struct { 346 h *commonHandler 347 buf *buffer.Buffer 348 freeBuf bool // should buf be freed? 349 sep string // separator to write before next key 350 prefix *buffer.Buffer // for text: key prefix 351 groups *[]string // pool-allocated slice of active groups, for ReplaceAttr 352} 353 354var groupPool = sync.Pool{New: func() any { 355 s := make([]string, 0, 10) 356 return &s 357}} 358 359func (h *commonHandler) newHandleState(buf *buffer.Buffer, freeBuf bool, sep string, prefix *buffer.Buffer) handleState { 360 s := handleState{ 361 h: h, 362 buf: buf, 363 freeBuf: freeBuf, 364 sep: sep, 365 prefix: prefix, 366 } 367 if h.opts.ReplaceAttr != nil { 368 s.groups = groupPool.Get().(*[]string) 369 *s.groups = append(*s.groups, h.groups[:h.nOpenGroups]...) 370 } 371 return s 372} 373 374func (s *handleState) free() { 375 if s.freeBuf { 376 s.buf.Free() 377 } 378 if gs := s.groups; gs != nil { 379 *gs = (*gs)[:0] 380 groupPool.Put(gs) 381 } 382} 383 384func (s *handleState) openGroups() { 385 for _, n := range s.h.groups[s.h.nOpenGroups:] { 386 s.openGroup(n) 387 } 388} 389 390// Separator for group names and keys. 391const keyComponentSep = '.' 392 393// openGroup starts a new group of attributes 394// with the given name. 395func (s *handleState) openGroup(name string) { 396 if s.h.json { 397 s.appendKey(name) 398 s.buf.WriteByte('{') 399 s.sep = "" 400 } else { 401 s.prefix.WriteString(name) 402 s.prefix.WriteByte(keyComponentSep) 403 } 404 // Collect group names for ReplaceAttr. 405 if s.groups != nil { 406 *s.groups = append(*s.groups, name) 407 } 408} 409 410// closeGroup ends the group with the given name. 411func (s *handleState) closeGroup(name string) { 412 if s.h.json { 413 s.buf.WriteByte('}') 414 } else { 415 (*s.prefix) = (*s.prefix)[:len(*s.prefix)-len(name)-1 /* for keyComponentSep */] 416 } 417 s.sep = s.h.attrSep() 418 if s.groups != nil { 419 *s.groups = (*s.groups)[:len(*s.groups)-1] 420 } 421} 422 423// appendAttr appends the Attr's key and value using app. 424// It handles replacement and checking for an empty key. 425// after replacement). 426func (s *handleState) appendAttr(a Attr) { 427 if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != KindGroup { 428 var gs []string 429 if s.groups != nil { 430 gs = *s.groups 431 } 432 // Resolve before calling ReplaceAttr, so the user doesn't have to. 433 a.Value = a.Value.Resolve() 434 a = rep(gs, a) 435 } 436 a.Value = a.Value.Resolve() 437 // Elide empty Attrs. 438 if a.isEmpty() { 439 return 440 } 441 // Special case: Source. 442 if v := a.Value; v.Kind() == KindAny { 443 if src, ok := v.Any().(*Source); ok { 444 if s.h.json { 445 a.Value = src.group() 446 } else { 447 a.Value = StringValue(fmt.Sprintf("%s:%d", src.File, src.Line)) 448 } 449 } 450 } 451 if a.Value.Kind() == KindGroup { 452 attrs := a.Value.Group() 453 // Output only non-empty groups. 454 if len(attrs) > 0 { 455 // Inline a group with an empty key. 456 if a.Key != "" { 457 s.openGroup(a.Key) 458 } 459 for _, aa := range attrs { 460 s.appendAttr(aa) 461 } 462 if a.Key != "" { 463 s.closeGroup(a.Key) 464 } 465 } 466 } else { 467 s.appendKey(a.Key) 468 s.appendValue(a.Value) 469 } 470} 471 472func (s *handleState) appendError(err error) { 473 s.appendString(fmt.Sprintf("!ERROR:%v", err)) 474} 475 476func (s *handleState) appendKey(key string) { 477 s.buf.WriteString(s.sep) 478 if s.prefix != nil { 479 // TODO: optimize by avoiding allocation. 480 s.appendString(string(*s.prefix) + key) 481 } else { 482 s.appendString(key) 483 } 484 if s.h.json { 485 s.buf.WriteByte(':') 486 } else { 487 s.buf.WriteByte('=') 488 } 489 s.sep = s.h.attrSep() 490} 491 492func (s *handleState) appendString(str string) { 493 if s.h.json { 494 s.buf.WriteByte('"') 495 *s.buf = appendEscapedJSONString(*s.buf, str) 496 s.buf.WriteByte('"') 497 } else { 498 // text 499 if needsQuoting(str) { 500 *s.buf = strconv.AppendQuote(*s.buf, str) 501 } else { 502 s.buf.WriteString(str) 503 } 504 } 505} 506 507func (s *handleState) appendValue(v Value) { 508 defer func() { 509 if r := recover(); r != nil { 510 // If it panics with a nil pointer, the most likely cases are 511 // an encoding.TextMarshaler or error fails to guard against nil, 512 // in which case "<nil>" seems to be the feasible choice. 513 // 514 // Adapted from the code in fmt/print.go. 515 if v := reflect.ValueOf(v.any); v.Kind() == reflect.Pointer && v.IsNil() { 516 s.appendString("<nil>") 517 return 518 } 519 520 // Otherwise just print the original panic message. 521 s.appendString(fmt.Sprintf("!PANIC: %v", r)) 522 } 523 }() 524 525 var err error 526 if s.h.json { 527 err = appendJSONValue(s, v) 528 } else { 529 err = appendTextValue(s, v) 530 } 531 if err != nil { 532 s.appendError(err) 533 } 534} 535 536func (s *handleState) appendTime(t time.Time) { 537 if s.h.json { 538 appendJSONTime(s, t) 539 } else { 540 writeTimeRFC3339Millis(s.buf, t) 541 } 542} 543 544// This takes half the time of Time.AppendFormat. 545func writeTimeRFC3339Millis(buf *buffer.Buffer, t time.Time) { 546 year, month, day := t.Date() 547 buf.WritePosIntWidth(year, 4) 548 buf.WriteByte('-') 549 buf.WritePosIntWidth(int(month), 2) 550 buf.WriteByte('-') 551 buf.WritePosIntWidth(day, 2) 552 buf.WriteByte('T') 553 hour, min, sec := t.Clock() 554 buf.WritePosIntWidth(hour, 2) 555 buf.WriteByte(':') 556 buf.WritePosIntWidth(min, 2) 557 buf.WriteByte(':') 558 buf.WritePosIntWidth(sec, 2) 559 ns := t.Nanosecond() 560 buf.WriteByte('.') 561 buf.WritePosIntWidth(ns/1e6, 3) 562 _, offsetSeconds := t.Zone() 563 if offsetSeconds == 0 { 564 buf.WriteByte('Z') 565 } else { 566 offsetMinutes := offsetSeconds / 60 567 if offsetMinutes < 0 { 568 buf.WriteByte('-') 569 offsetMinutes = -offsetMinutes 570 } else { 571 buf.WriteByte('+') 572 } 573 buf.WritePosIntWidth(offsetMinutes/60, 2) 574 buf.WriteByte(':') 575 buf.WritePosIntWidth(offsetMinutes%60, 2) 576 } 577}