about summary refs log tree commit diff
path: root/src/state/models/log.ts
blob: f2709f2f1cb0b2f91e9bd698af059dbb5b2ad045 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import {makeAutoObservable} from 'mobx'
import {XRPCError, XRPCInvalidResponseError} from '@atproto/xrpc'
import {isObj, hasProp} from 'lib/type-guards'

interface LogEntry {
  id: string
  type?: string
  summary?: string
  details?: string
  ts?: number
}

let _lastTs: string
let _lastId: string
function genId(): string {
  let candidate = String(Date.now())
  if (_lastTs === candidate) {
    const id = _lastId + 'x'
    _lastId = id
    return id
  }
  _lastTs = candidate
  _lastId = candidate
  return candidate
}

export class LogModel {
  entries: LogEntry[] = []

  constructor() {
    makeAutoObservable(this, {serialize: false, hydrate: false})
  }

  serialize(): unknown {
    return {
      entries: this.entries.slice(-100),
    }
  }

  hydrate(v: unknown) {
    if (isObj(v)) {
      if (hasProp(v, 'entries') && Array.isArray(v.entries)) {
        this.entries = v.entries.filter(
          e => isObj(e) && hasProp(e, 'id') && typeof e.id === 'string',
        )
      }
    }
  }

  private add(entry: LogEntry) {
    this.entries.push(entry)
  }

  debug(summary: string, details?: any) {
    details = detailsToStr(details)
    console.debug(summary, details || '')
    this.add({
      id: genId(),
      type: 'debug',
      summary,
      details,
      ts: Date.now(),
    })
  }

  warn(summary: string, details?: any) {
    details = detailsToStr(details)
    console.debug(summary, details || '')
    this.add({
      id: genId(),
      type: 'warn',
      summary,
      details,
      ts: Date.now(),
    })
  }

  error(summary: string, details?: any) {
    details = detailsToStr(details)
    console.debug(summary, details || '')
    this.add({
      id: genId(),
      type: 'error',
      summary,
      details,
      ts: Date.now(),
    })
  }
}

function detailsToStr(details?: any) {
  if (details && typeof details !== 'string') {
    if (
      details instanceof XRPCInvalidResponseError ||
      details.constructor.name === 'XRPCInvalidResponseError'
    ) {
      return `The server gave an ill-formatted response.\nMethod: ${
        details.lexiconNsid
      }.\nError: ${details.validationError.toString()}`
    } else if (
      details instanceof XRPCError ||
      details.constructor.name === 'XRPCError'
    ) {
      return `An XRPC error occurred.\nStatus: ${details.status}\nError: ${details.error}\nMessage: ${details.message}`
    } else if (details instanceof Error) {
      return details.toString()
    }
    return JSON.stringify(details, null, 2)
  }
  return details
}