@xiaoqiong0v0/opencode-plugin-logger 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/logger.js +96 -0
- package/package.json +13 -0
package/logger.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync, existsSync, readdirSync, unlinkSync, statSync, readFileSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { homedir } from "node:os"
|
|
4
|
+
|
|
5
|
+
const pad = (n, w = 2) => String(n).padStart(w, "0")
|
|
6
|
+
|
|
7
|
+
const GLOBAL_DIR = join(homedir(), ".config", "opencode")
|
|
8
|
+
const CONFIG_NAME = "plugin-logger.jsonc"
|
|
9
|
+
const CONFIG_PATH = join(GLOBAL_DIR, CONFIG_NAME)
|
|
10
|
+
|
|
11
|
+
const defaults = {
|
|
12
|
+
dir: join(homedir(), ".opencode", "plugins-log"),
|
|
13
|
+
timeFormat: "yyyy-MM-dd HH:mm:ss",
|
|
14
|
+
retentionDays: 7,
|
|
15
|
+
enabled: false,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const SAMPLE_CFG = `{
|
|
19
|
+
// 是否启用日志输出(默认 false)
|
|
20
|
+
"enabled": false,
|
|
21
|
+
// 日志文件输出目录
|
|
22
|
+
"dir": "${defaults.dir.replace(/\\/g, "/")}",
|
|
23
|
+
// 时间格式,SSS 为毫秒
|
|
24
|
+
"timeFormat": "${defaults.timeFormat}.SSS",
|
|
25
|
+
// 日志保留天数,0 为永不过期
|
|
26
|
+
"retentionDays": ${defaults.retentionDays}
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
|
|
30
|
+
function readJsonc(path) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = readFileSync(path, "utf-8").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "")
|
|
33
|
+
return JSON.parse(raw)
|
|
34
|
+
} catch { return {} }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveCfg(opts) {
|
|
38
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
39
|
+
try { writeFileSync(CONFIG_PATH, SAMPLE_CFG, "utf-8") } catch {}
|
|
40
|
+
}
|
|
41
|
+
const cfg = existsSync(CONFIG_PATH) ? readJsonc(CONFIG_PATH) : {}
|
|
42
|
+
return { ...defaults, ...cfg, ...(opts || {}) }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default function createLogger(name, opts) {
|
|
46
|
+
const cfg = resolveCfg(opts)
|
|
47
|
+
if (!cfg.enabled) return { loaded() {}, info() {}, error() {}, hook() {}, tool() {} }
|
|
48
|
+
|
|
49
|
+
const logDir = cfg.dir
|
|
50
|
+
if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true })
|
|
51
|
+
|
|
52
|
+
const formatTime = (ts) => {
|
|
53
|
+
const d = new Date(ts)
|
|
54
|
+
let s = cfg.timeFormat
|
|
55
|
+
s = s.replace("yyyy", d.getFullYear())
|
|
56
|
+
s = s.replace("MM", pad(d.getMonth() + 1))
|
|
57
|
+
s = s.replace("dd", pad(d.getDate()))
|
|
58
|
+
s = s.replace("HH", pad(d.getHours()))
|
|
59
|
+
s = s.replace("mm", pad(d.getMinutes()))
|
|
60
|
+
s = s.replace("ss", pad(d.getSeconds()))
|
|
61
|
+
s = s.replace("SSS", pad(d.getMilliseconds(), 3))
|
|
62
|
+
return s
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const cleanOld = () => {
|
|
66
|
+
if (!cfg.retentionDays) return
|
|
67
|
+
const maxAge = cfg.retentionDays * 86400000
|
|
68
|
+
const now = Date.now()
|
|
69
|
+
try {
|
|
70
|
+
for (const f of readdirSync(logDir)) {
|
|
71
|
+
if (!f.match(/^\d{4}-\d{2}-\d{2}\.log$/)) continue
|
|
72
|
+
try { if (now - statSync(join(logDir, f)).mtimeMs > maxAge) unlinkSync(join(logDir, f)) } catch {}
|
|
73
|
+
}
|
|
74
|
+
} catch {}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let currentDay = formatTime(Date.now()).slice(0, 10)
|
|
78
|
+
let logFile = join(logDir, currentDay.replace(/-/g, "") + ".log")
|
|
79
|
+
|
|
80
|
+
const write = (level, msg) => {
|
|
81
|
+
const now = Date.now()
|
|
82
|
+
const day = formatTime(now).slice(0, 10)
|
|
83
|
+
if (day !== currentDay) { currentDay = day; logFile = join(logDir, day.replace(/-/g, "") + ".log"); cleanOld() }
|
|
84
|
+
try { appendFileSync(logFile, `[${formatTime(now)}] [${level}] ${name} ${msg}\n`) } catch {}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
cleanOld()
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
loaded: () => write("INFO", "loaded"),
|
|
91
|
+
info: (msg) => write("INFO", msg),
|
|
92
|
+
error: (msg, err) => write("ERROR", err ? `${msg} — ${err.message || err}` : msg),
|
|
93
|
+
hook: (h, d) => write("HOOK", `${h}${d ? " → " + d : ""}`),
|
|
94
|
+
tool: (t, a) => write("TOOL", `${t}(${JSON.stringify(a).slice(0, 200)})`),
|
|
95
|
+
}
|
|
96
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xiaoqiong0v0/opencode-plugin-logger",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Configurable file-based logger for OpenCode plugins",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./logger.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"logger.js"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT"
|
|
13
|
+
}
|