{"id":23790030,"url":"https://github.com/blackglory/logger","last_synced_at":"2026-04-05T23:02:50.446Z","repository":{"id":44955703,"uuid":"307658135","full_name":"BlackGlory/logger","owner":"BlackGlory","description":"🌲","archived":false,"fork":false,"pushed_at":"2025-06-17T07:58:35.000Z","size":3215,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-04T22:38:26.057Z","etag":null,"topics":["docker-image","esm","microservice","nodejs","typescript"],"latest_commit_sha":null,"homepage":"https://hub.docker.com/r/blackglory/logger","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BlackGlory.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-10-27T10:08:09.000Z","updated_at":"2025-06-17T07:58:39.000Z","dependencies_parsed_at":"2025-05-24T10:24:50.347Z","dependency_job_id":"cff0e286-833c-4064-8ddb-cedbf273f982","html_url":"https://github.com/BlackGlory/logger","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/BlackGlory/logger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Flogger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Flogger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Flogger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Flogger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BlackGlory","download_url":"https://codeload.github.com/BlackGlory/logger/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Flogger/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31452901,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"ssl_error","status_checked_at":"2026-04-05T21:22:51.943Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker-image","esm","microservice","nodejs","typescript"],"created_at":"2025-01-01T17:18:10.817Z","updated_at":"2026-04-05T23:02:50.441Z","avatar_url":"https://github.com/BlackGlory.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Logger\n提供以HTTP为通讯协议的日志服务.\n\n## Install\n### 从源代码运行\n```sh\ngit clone https://github.com/BlackGlory/logger\ncd log\nyarn install\nyarn build\nyarn bundle\nyarn --silent start\n```\n\n### 从源代码构建\n```sh\ngit clone https://github.com/BlackGlory/logger\ncd logger\nyarn install\nyarn docker:build\n```\n\n### Recipes\n#### docker-compose.yml\n```yaml\nversion: '3.8'\n\nservices:\n  logger:\n    image: 'blackglory/logger'\n    restart: always\n    volumes:\n      - 'logger-data:/data'\n    ports:\n      - '8080:8080'\n\nvolumes:\n  logger-data:\n```\n\n## API\n- 记录器id需要满足此正则表达式: `^[a-zA-Z0-9\\.\\-_]{0,255}$`\n- 日志id的格式为`Unix毫秒时间戳-从0开始的计数器`.\n  由于日志可以被删除, 因此不应假设计数器的数字是连续的.\n\n### get all logger ids\n`GET /loggers`\n\n返回JSON:\n```ts\nstring[]\n```\n\n#### Example\n##### curl\n```sh\ncurl 'http://localhost:8080/loggers'\n```\n\n##### JavaScript\n```js\nawait fetch('http://localhost:8080/loggers')\n  .then(res =\u003e res.json())\n```\n\n### get logger\n`GET /loggers/\u003cid\u003e`\n\n获取记录器的信息.\n\n如果记录器未启用, 返回HTTP状态码404.\n\n返回JSON:\n```ts\n{\n  timeToLive: number | null // null表示Infinity\n  limit: number | null // null表示Infinity\n}\n```\n\n#### Example\n##### curl\n```sh\ncurl \"http://localhost:8080/loggers/$id\"\n```\n\n##### fetch\n```js\nawait fetch(`http://localhost:8080/loggers/${id}`)\n  .then(res =\u003e res.json())\n```\n\n### set logger\n`PUT /loggers/\u003cid\u003e`\n\n配置记录器.\n如果记录器曾经被配置过, 则会应用新的设置, 记录器内的日志清理会立即发生.\n\n发送JSON:\n```ts\n{\n  // 一条日志自写入起开始计算的存活时间(毫秒).\n  // null表示Infinity\n  timeToLive: number | null\n\n  // 记录器的日志数量限制, 当日志数量超出此值时, 较早的日志会被删除.\n  // null表示Infinity, 配置为0通常没有意义.\n  limit: number | null\n}\n```\n\n#### Example\n##### curl\n```sh\ncurl \\\n  --request PUT \\\n  --header \"Content-Type: application/json\" \\\n  --data \"$payload\" \\\n  \"http://localhost:8080/loggers/$id\"\n```\n\n##### fetch\n```js\nawait fetch(`http://localhost:8080/loggers/${id}`, {\n  method: 'PUT'\n, headers: {\n    'Content-Type': 'application/json'\n  }\n, body: JSON.stringify(config)\n})\n```\n\n### remove logger\n`DELETE /loggers/\u003cid\u003e`\n\n#### Example\n##### curl\n```sh\ncurl \\\n  --request DELETE \\\n  \"http://localhost:8080/loggers/$id\"\n```\n\n##### fetch\n```js\nawait fetch(`http://localhost:8080/loggers/${id}`, {\n  method: 'DELETE'\n})\n```\n\n### log\n`POST /loggers/\u003cid\u003e/log`\n\n往特定记录器写入一条日志.\n\n发送JSON:\n```ts\nJSONValue\n```\n\n#### Example\n##### curl\n```sh\ncurl \\\n  --request POST \\\n  --header \"Content-Type: application/json\" \\\n  --data \"$value\" \\\n  \"http://localhost:8080/loggers/$id/log\"\n```\n\n##### JavaScript\n```js\nawait fetch(`http://localhost:8080/loggers/${id}/log`, {\n  method: 'POST'\n, headers: {\n    'Content-Type': 'application/json'\n  }\n, body: JSON.stringify(value)\n})\n```\n\n### follow\n`GET /loggers/\u003cid\u003e/follow`\n\n通过Server-Sent Events(SSE)订阅特定记录器.\n如果记录器不存在, 服务器会返回404, `EventSource`会因此放弃重新连接.\n如果记录器被删除, 服务器会结束响应, 然后`EventSource`会尝试重新连接一次,\n由于这次重新连接会得到404, `EventSource`会因此放弃重新连接.\n\n可用以下两种方式从从特定logId开始接收日志, 客户端总是可以捏造不存在的logId:\n- SSE的请求头`Last-Event-Id`.\n- URL参数`since=\u003clogId\u003e`.\n  此方法存在是因为大部分`EventSource`无法自定义请求头`Last-Event-Id`.\n  `since`参数的优先级低于`Last-Event-Id`.\n\n日志的顺序受到强制保证, 若在接收完旧日志前就有新日志被写入, 则新日志会阻塞直至旧日志都传输结束.\n\n收到的每条JSON:\n```ts\nJSONValue\n```\n\n#### Example\n##### sse-cat\n```sh\nsse-cat \"http://localhost:8080/loggers/$id/follow\"\n```\n\n##### JavaScript\n```js\nconst es = new EventSource(`http://localhost:8080/loggers/${id}`)\nes.addEventListener('message', event =\u003e {\n  const value = JSON.parse(event.data)\n  console.log(value)\n})\n```\n\n### get logs\n`GET /loggers/\u003cid\u003e/logs/\u003clogIds\u003e`\n\n多个logId用`,`隔开.\n如果log不存在, 则对应位置会以`null`表示.\n\n返回JSON:\n```ts\nArray\u003cJSONValue | null\u003e\n```\n\n#### Example\n##### curl\n```sh\ncurl \"http://localhost:8080/loggers/$id/logs/$logIds\"\n```\n\n##### JavaScript\n```js\nawait fetch(`http://localhost:8080/loggers/${id}/logs/${logIds}`)\n```\n\n### delete logs\n`DELETE /loggers/\u003cid\u003e/logs/\u003clogIds\u003e`\n\n多个logId用`,`隔开.\n\n#### Example\n##### curl\n```sh\ncurl \\\n  --request DELETE \\\n  \"http://localhost:8080/loggers/$id/logs/$logIds\"\n```\n\n##### JavaScript\n```js\nawait fetch(`http://localhost:8080/loggers/${id}/logs/${logIds}`, {\n  method: 'DELETE'\n})\n```\n\n### query logs\n`GET /loggers/\u003cid\u003e/logs`\n\n按范围查询日志.\n参数允许捏造不存在的`logId`.\n\n参数:\n- `order=\u003casc|desc\u003e`: 必选, 排序方式, 返回结果的顺序也会受到影响.\n- `from=\u003clogId\u003e`: 可选, 设定范围的起始点, 省略时指代最早的logId.\n  `from`的logId需要小于或等于`to`的logId.\n- `to=\u003clogId\u003e`: 可选, 设定范围的结束点, 省略时指代最晚的logId.\n  `to`的logId需要大于或等于`from`的logId.\n- `skip=\u003cnumber\u003e`: 可选, 跳过number个日志, 省略时意为`0`.\n- `limit=\u003cnumber\u003e`: 可选, 限制返回结果的数量为number个, 省略时意为不限制.\n\n返回JSON:\n```ts\nArray\u003c{\n  id: string\n  value: string\n}\u003e\n```\n\n#### Example\n##### curl\n```sh\ncurl \"http://localhost:8080/loggers/$id/logs?order=asc\"\n```\n\n##### JavaScript\n```js\nawait fetch(`http://localhost:8080/loggers/${id}/logs?order=asc`)\n  .then(res =\u003e res.json())\n```\n\n### clear logs\n`DELETE /loggers/\u003cid\u003e/logs`\n\n按范围查询日志.\n参数允许捏造不存在的`logId`.\n\n参数:\n- `order=\u003casc|desc\u003e`: 当提供其他参数时必选, 排序方式.\n- `from=\u003clogId\u003e`: 可选, 设定范围的起始点, 省略时指代最早的logId.\n  `from`的logId需要小于或等于`to`的logId.\n- `to=\u003clogId\u003e`: 可选, 设定范围的结束点, 省略时指代最晚的logId.\n  `to`的logId需要大于或等于`from`的logId.\n- `skip=\u003cnumber\u003e`: 可选, 跳过number个日志, 省略时意为`0`.\n- `limit=\u003cnumber\u003e`: 可选, 限制返回结果的数量为number个, 省略时意为不限制.\n\n#### Example\n##### curl\n```sh\ncurl \\\n  --request DELETE \\\n  \"http://localhost:8080/loggers/$id/logs\"\n```\n\n##### JavaScript\n```js\nawait fetch(`http://localhost:8080/loggers/${id}/logs`, {\n  method: 'DELETE'\n})\n```\n\n## 环境变量\n### `LOGGER_HOST`, `LOGGER_PORT`\n通过环境变量`LOGGER_HOST`和`LOGGER_PORT`决定服务器监听的地址和端口,\n默认值为`localhost`和`8080`.\n\n### heartbeat\n#### `LOGGER_SSE_HEARTBEAT_INTERVAL`\n通过环境变量`LOGGER_SSE_HEARTBEAT_INTERVAL`可以设置SSE心跳包的发送间隔, 单位为毫秒.\n在默认情况下, 服务不会发送SSE心跳包,\n半开连接的检测依赖于服务端和客户端的运行平台的TCP Keepalive配置.\n\n当`LOGGER_SSE_HEARTBEAT_INTERVAL`大于零时,\n服务会通过SSE的heartbeat事件按指定间隔发送空白数据.\n客户端若要实现半开连接检测, 则需要自行根据heartbeat事件设定计时器, 以判断连接是否正常.\n\n## 客户端\n- JavaScript/TypeScript(Node.js, Browser): \u003chttps://github.com/BlackGlory/logger-js\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackglory%2Flogger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblackglory%2Flogger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackglory%2Flogger/lists"}