Project Icon

logger

轻量级Node.js日志工具 适用于LLM应用开发

SmolLogger是一款轻量级Node.js日志工具,专为提示工程和LLM应用开发设计。该工具支持文件系统和控制台日志记录,能够捕获提示与响应对,并提供CLI导出功能。SmolLogger具有零依赖、TypeScript支持和高度可自定义性,适用于应用开发、提示工程和生产环境。通过将IDE作为日志UI,SmolLogger为开发者提供了快速、简单且可扩展的日志记录方案。

2024 注意:即使之前能正常工作,现在日志记录器的绑定不再有效 - 我们还不太清楚原因 图片

🐤 smol logger

一个用于提示/LLM应用工程的最小可行日志记录器。

使用你的IDE作为日志UI - 一个快速、简单、可扩展、零依赖的Node.js日志工具。

  • 在应用开发中:一个快速的日志工具,使用文件系统作为日志UI
  • 在提示工程中:包装和转换对OpenAI等的异步调用,以清晰地捕获提示与响应
    • CLI可将日志导出为.tsv文件以导入电子表格。电子表格就是你进行提示工程所需的全部工具
  • 在生产环境中:易于扩展以将日志发送到日志存储如Logflare
图片

简短视频演示:https://www.loom.com/share/ae818b7d058343c1ad6caf8deee2e430

特性

  • 默认情况下,同时记录到终端和本地json文件,便于导航/版本控制
    • 自动记录经过的时间、文件路径、行号、日志调用顺序
    • 通过删除.logs文件夹清除日志(可自定义)
    • CLI可将json日志编译为.tsv文件以导入电子表格(如Google Sheets、Excel、Quadratic)
  • 包装和转换异步调用以捕获提示与响应对
  • 可扩展
    • 将日志持久化到远程存储,如LogFlare
    • 自定义从命名/缩进到终端日志颜色的一切
  • 零依赖,核心代码不到100行。比Winston和Bunyan更快(参见基准测试)
  • 使用Typescript,非常流行
  • MIT开源:https://github.com/smol-ai/logger -(待完成)由Codium AI进行测试

非目标:

  • 无日志级别。太复杂 - 只需添加描述性的日志名称,要么发送所有日志,要么一条不发
  • 暂不考虑在浏览器中运行。如果想讨论,请开一个issue!不确定将文件存储替换为其他方式会有多复杂。

使用方法

安装:

npm install @smol-ai/logger

使用:

import { SmolLogger } from '@smol-ai/logger';

const logger = new SmolLogger({ logToConsole: true, logToStore: true }) // 易于关闭
const log = logger.log // 可选的便捷别名,减少冗长

log('日志名称(必填)', payload) // 基本用法

默认情况下,所有日志都会发送到控制台和文件系统,因此你可以在IDE中轻松导航:

图片

日志看起来是这样的!

图片

log函数是一个单一参数的"恒等函数" - 返回payload,因此你可以就地修改。

mySingleArityFunction({foo, bar, baz}) // 哦不,我需要记录bar和baz
mySingleArityFunction({foo, ...log({bar, baz})) // 搞定!
mySingleArityFunction(log({foo, bar, baz})) // 为什么不全部记录呢

myBadMultiArityFunction(foo, bar, baz) // 哦不,我需要记录bar
myBadMultiArityFunction(foo, log(bar), baz) // 搞定!
myBadMultiArityFunction(...log({foo, bar, baz}).values()) // 为什么不全部记录呢

我们默认使用单一参数以鼓励JS生态系统中的这种做法

清除日志

要清除你的日志 - 删除.logs文件夹!就这么简单!下次运行日志时它会重新生成。

生产环境:远程存储

对于生产环境的日志记录,可以覆盖Smol Logger的存储目标。我们喜欢Logflare

// 可选:保留默认的本地文件存储以便重用
const localStore = logger.store

// 用你自己的远程文件存储覆盖默认的本地文件存储
// { logName: string, loggedLine: string | null, payload: any, secondsSinceStart: number, secondsSinceLastLog: number }
logger.store = ({ logName, loggedLine, payload, secondsSinceStart, secondsSinceLastLog }) => {
  fetch("https://api.logflare.app/logs/json?source=YOUR_SOURCE_ID", {
    method: "POST",
    headers: {
      "Content-Type": "application/json; charset=utf-8",
      "X-API-KEY": "YOUR_API_KEY_HERE"
    },
    body: JSON.stringify({ message: logName, metadata: {loggedLine, payload, secondsSinceStart, secondsSinceLastLog }})
  })
  // 可选:同时记录到本地文件存储
  localStore({ logName, loggedLine, payload, secondsSinceStart, secondsSinceLastLog })
}
图片

如果你预期会有大量日志,应该对它们进行批处理:

// 这是目前未经测试的示例代码,如果你运行并尝试过,请发送PR
const logMessages = []

function throttle(func, delay = 1000) {
  let timeout = null;
  return function(...args) {
    const { logName, loggedLine, payload, secondsSinceStart, secondsSinceLastLog } = args;
    logMessages.push(({ message: logName, metadata: {loggedLine, payload, secondsSinceStart, secondsSinceLastLog }}));
    if (!timeout) {
      timeout = setTimeout(() => {
        func.call(this, ...args);
        timeout = null;
      }, delay);
    }
  };
}

const sendToLogFlare = ({ logName, loggedLine, payload, secondsSinceStart, secondsSinceLastLog }) => { fetch("https://api.logflare.app/logs/json?source=YOUR_SOURCE_ID", { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8", "X-API-KEY": "YOUR_API_KEY" }, body: JSON.stringify({"batch": logMessages}) }) .then(() => logMessages = []) }

log.store = throttle(sendToLogFlare, 1000)


<details>
<summary>
<strong>异步/阻塞日志记录</strong>
</summary>

在 smol logger 中,日志记录默认是同步的。

请注意,在上面的例子中,我们在同步调用内部触发了一个异步fetch。如果你的应用程序崩溃,由于它是异步运行的,有一个很小的可能性日志可能无法完成发送。如果你需要为异步调用进行阻塞,你可以使用 `asyncLog` 方法和一个异步存储:

```js
// 可选:存储本地文件存储以便重用
const oldStore = logger.store

// 用你自己的远程文件存储覆盖默认的本地文件存储
// { logName: string, loggedLine: string | null, payload: any, secondsSinceStart: number, secondsSinceLastLog: number }
logger.store = async ({ logName, loggedLine, payload, secondsSinceStart, secondsSinceLastLog }) => {
  const res = await fetch("https://api.logflare.app/logs/json?source=YOUR_SOURCE_ID", {
    method: "POST",
    headers: {
      "Content-Type": "application/json; charset=utf-8",
      "X-API-KEY": "YOUR_API_KEY_HERE"
    },
    body: JSON.stringify({ message: logName, metadata: {loggedLine, payload, secondsSinceStart, secondsSinceLastLog }})
  }).then(res => res.json())
  // 只是演示你可以在这个存储中使用await
}

// 现在你可以在异步上下文中阻塞执行
await logger.asyncLog('我的消息在这里', { foo: 'bar' })

注意:这个功能是新的且未经测试,请尝试并提供修复/反馈

LLM应用:拦截输入与输出

这会同时记录你想要监控的异步函数的输入和输出。主要用于提示工程,在这种情况下你非常关心输入与输出对在同一日志文件中的可见性。

import OpenAI from 'openai'; // 这是用于openai v4包的!v3说明在下面
import {SmolLogger} from '@smol-ai/logger';

const openai = new OpenAI({
  apiKey: 'my api key', // 默认为 process.env["OPENAI_API_KEY"]
});
const logger = new SmolLogger({logToConsole: true, logToStore: true}); // 两个参数都是可选的,只是为你列出默认值以便轻松关闭

const wrapped = logger.wrap(
  openai.chat.completions.create.bind(openai) // 绑定很重要,因为OpenAI内部是如何检索其配置的

async function main() {
  const completion = await wrapped({
    model: "gpt-3.5-turbo",
    messages: [
      {
        role: "system",
        content: "你是一个有帮助的助手",
      },
      {
        role: "user",
        content: "选择一个著名的流行歌手,并给我他们的3首歌",
      },
    ],
  });

  console.log(completion.choices);
}

main();
Openai SDK V3说明

很快就会过时,所以我们把它隐藏在这里

import { Configuration, OpenAIApi } from 'openai';
const openai = new OpenAIApi(new Configuration({ apiKey: process.env.OPENAI_API_KEY }));

const wrapped = logger.wrap(openai.createChatCompletion.bind(openai)) // 绑定很重要,因为OpenAI内部是如何检索其配置的
const response = await wrapped({
        model: process.env.OPENAI_MODEL || 'gpt-3.5-turbo',
        messages: [/* 等等 */ ],
    });

有时输出可能非常冗长(就像OpenAI chatCompletion的情况)。所以我们还允许你暴露一个简单的"日志转换器",这是一个任意函数,可以将拦截的输出修改为你喜欢的格式:

// 编辑上面的代码以添加logTransformer
const wrapped = logger.wrap(
  openai.createChatCompletion.bind(openai),  // 绑定很重要,因为OpenAI内部是如何检索其配置的
  { 
    wrapLogName: 'chatGPT APIcall', // 可选 - 自定义显示在日志上的名称。默认为 "wrap(fn.name)"
    logTransformer: (args, result) => ({ // 可以是异步的
      // ...result, // 可选 - 如果你想要完整的原始结果本身
      prompt: args[0].messages,
      response: result.choices[0].message, // 这是v4 api;v3是 result.data.choices[0].message
    })
  }
)
const response = await wrapped({
        model: process.env.OPENAI_MODEL || 'gpt-3.5-turbo',
        messages: [/* 等等 */ ],
    });

// 现在记录的响应只包含我们感兴趣的特定字段,见下面的截图
image image

log2tsv CLI

单个JSON日志文件对于调试单次运行很有用。这是smol-logger的良好默认设置。

对于提示工程,你还需要轻松比较不同运行之间的提示与输出,并对它们进行评分/编写评估。对于这一点,没有比电子表格更灵活或更强大的界面了。因此,我们帮助你将日志导出为电子表格。

你可以运行 log2tsv CLI,它会在你的 ./logs 文件夹中输出一个 logs.tsv 文件(我们会接受PR来自定义这个)。你可以在Google Sheets/Excel/Quadratic等中导入这个 .tsv 文件,以进行进一步的提示工程。

./node_modules/.bin/log2tsv # 在安装了 @smol-ai/logger 的目录中

你也可以将其放入npm脚本中,它就会运行:

// package.json
{
  "scripts": {
    "tsv": "log2tsv" // 然后运行 npm run tsv
  }
}
image

请注意,标题是从第一条日志中提取的 - 很可能不会完全匹配所有日志的主体内容,特别是当你有不规则形状的日志时。我们相信你能够自行在电子表格中重新命名它们,如果它们足够重要的话。

如果你需要,我们会接受一个PR来使其可以通过编程方式运行(而不仅仅是CLI)。

自定义其他内容

自定义的一般规则是覆盖类中暴露的任何变量和方法:

logger.logDirectory = '.smol-logs' // 更改默认文件存储目录

logger.logToConsole = false // 关闭终端日志记录
logger.logToStore = false // 关闭存储日志记录
// 更多使用想法
logger.logToStore = Math.random() < 0.05 // 只记录5%的流量
logger.logToStore = user.isVIPCustomer() // 基于功能标志记录日志

logger.logName = (name: string) => `我的自定义日志名称:${name}` // 更改日志命名!

logger.LOGCOLOR = (logName: string) => "\x1b[35m" + logName + "\x1b[0m"; // 将日志颜色设置为洋红色而不是黄色

其他可以尝试的日志颜色:

\x1b[31m: 红色
\x1b[32m: 绿色
\x1b[33m: 黄色
\x1b[34m: 蓝色
\x1b[35m: 洋红色
\x1b[36m: 青色
\x1b[37m: 白色
发挥创意!例如,你可以在单个归约函数中堆叠logName函数...
// 对于高级应用,你可以在堆栈中添加日志名称
const logStack = [logger.logName] // 将原始logName函数存储在堆栈底部
logger.logName = (name) => logStack.reduce((prev, fn) => fn(prev), name)
let temp = 0
do {
  logStack.unshift(name => '   ' + name)
  // 这里记录的所有内容都缩进一级
  log('logname1 here ' + temp, temp)
    let temp2 = 0
    do {
      logStack.unshift(name => '   ' + name)
      // 这里记录的所有内容都缩进两级
      log('logname2 here ' + temp2, temp2)
      logStack.shift()
    } while (temp2++ < 5)
  logStack.shift()
} while (temp++ < 5)
image

将来我们可能会为嵌套日志记录提供更正式的API: (这在代码中但未经测试)

const sublog = logger.newSubLog('prefix') // Logger的新实例,带有缩进日志设置
let temp = 0
do {
  sublog('logname1 here ' + temp, temp) // 这里记录的所有内容都缩进一级
  const sublog2 = sublog.sublog()
    let temp2 = 0
    do {
      sublog2('logname2 here ' + temp2, temp2) // 这里记录的所有内容都缩进两级
    } while (temp2++ < 5)
} while (temp++ < 5)

如果感兴趣,请开启一个issue,这里有很多设计空间

基准测试

我们并不是真的追求最高速度,因为我们更关心开发中的开发者体验,但请参见 /benchmark

$ cd benchmark && npm install
$ node bench.js > bench.txt   
$ grep "^bench" bench.txt
benchWinston*100000: 1.690s
benchBunyan*100000: 1.820s
benchPino*100000: 892.089ms
benchSmol*100000: 1.290s
benchWinston*100000: 1.620s
benchBunyan*100000: 1.712s
benchPino*100000: 911.538ms
benchSmol*100000: 1.284s

这希望能证明我们比Winston/Bunyan更快,并且与Pino相比具有竞争力,同时提供更好的功能集。

贡献者注意事项

这个仓库是使用 https://github.com/alexjoverm/typescript-library-starter 初始化的(必须使用 https://github.com/alexjoverm/typescript-library-starter/issues/333 进行修改)

发布

按照控制台说明安装semantic release并运行它(对"Do you want a .travis.yml file with semantic-release setup?"问题回答NO)。

注意:确保你已经在package.json文件中设置了repository.url

npm install -g semantic-release-cli
semantic-release-cli setup
# 重要!!对"Do you want a `.travis.yml` file with semantic-release setup?"问题回答NO。它已经为你准备好了 :P

从现在开始,你需要使用npm run commit,这是创建常规提交的便捷方式。

自动发布得益于semantic release,它会自动在githubnpm上发布你的代码,并自动生成更新日志。

swyx注 - 无法使发布工作正常,一直失败。

目前:

npm run build
npm version patch
npm publish --access=public
项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

稿定AI

稿定设计 是一个多功能的在线设计和创意平台,提供广泛的设计工具和资源,以满足不同用户的需求。从专业的图形设计师到普通用户,无论是进行图片处理、智能抠图、H5页面制作还是视频剪辑,稿定设计都能提供简单、高效的解决方案。该平台以其用户友好的界面和强大的功能集合,帮助用户轻松实现创意设计。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号