npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@yolanda-qn/logger

v1.0.2

Published

H5日志库

Downloads

1

Readme

h5实时日志记录

安装

yarn add @yolanda-qn/logger

示例

运行项目根目录下的 index.html 文件

使用

import Logger, {
  transpilers,
  WebHttp,
  WebStorage,
  TypeEnum,
  LevelEnum,
  RunEnvEnum,
} from '@yolanda-qn/logger';

const logger = new Logger({
  // 等级顺序为 error > warn > debug > log > info
  // 默认 LevelEnum.log
  logLevel: LevelEnum.log,
});
const recordToServerTranspiler = new transpilers.RecordToServer({
  // 服务器地址, post请求
  url: 'https://test.com/api/log',
  // 发送数据前的数据转换
  transform: (mdatas) => ({ logs: mdatas }),
});
// 提供数据发送能力
recordToServerTranspiler.useHttp(WebHttp);
// 从 localStorage 读取缓存的数据和存储新的数据到 localStorage
recordToServerTranspiler.useStorage(WebStorage);

// 使用 console 打印信息
logger.useTranspiler(new transpilers.Console());
logger.useTranspiler(recordToServerTranspiler);


// 开始记录日志
const mdata = {
  app_id: 'test_app', // 标识各类APP。必填
  type: TypeEnum.request, // 日志分类。必填
  level: LevelEnum.debug, // 日志等级。必填
  path: location.href, // 页面地址
  message: '发送数据', // 日志信息。必填
  context: 'fetchListData', // 上下文。
  stack: '这是一个错误堆栈', // 错误日志可能需要这个。
  user_id: 12345678, // 用户身份标识。
  system_type: 'android', // 系统类型。例如 ios, android等。
  system_version: '11', // 系统版本
  run_env: RunEnvEnum.webview, // 运行环境
  app_version: '', // app版本
  h5_version: '1.0.0', // H5版本
  user_agent: navigator.userAgent, // 一般浏览器和webview环境是可以拿到ua的
  tags: 'test', // 额外标签。多个标签使用 , 分割。可被用于搜索
};

// 除第一个参数外的其他信息会被转为JsonString拼接到message
// 不满足格式的数据会被舍弃

logger.stash(mdata, '其它信息1', { foo: 'foo' });

// level固定为info
logger.info(mdata, '其它信息1', { foo: 'foo' });

// level固定为log
logger.log(mdata, '其它信息1', { foo: 'foo' });


// level固定为debug
logger.debug(mdata, '其它信息1', { foo: 'foo' });

// level固定为warn
logger.warn(mdata, '其它信息1', { foo: 'foo' });

// level固定为error
logger.error(mdata, '其它信息1', { foo: 'foo' });


// 使用方式基本等同于 console
// 下列参数会合并到 message 字段
logger[level](1, 'string', { foo: 1 }, ['2', 3]);

exports

export { default } from './Logger';

export { default as Console } from './Console';
export { default as Metadata } from './Metadata';
export { default as Transpiler } from './Transpiler';
export { default as Http } from './Http';
export { default as WebHttp } from './WebHttp';
export { default as Storage } from './Storage';
export { default as WebStorage } from './WebStorage';
export { TypeEnum } from './enums/type.enum';
export { LevelEnum } from './enums/level.enum';
export { RunEnvEnum } from './enums/run-env.enum';
export { default as transpilers } from './transpilers';
export { default as version } from './version';

Logger

verbose

是否显示调试信息

  • 类型: boolean

setDefaultMetadata

设置默认元数据,例如app_id, user_id, run_env, system_type, system_version, app_version, h5_version 等等。传入的日志数据会与之合并

  • 参数:Partial<Metadata>

setLogLevel

对于 RecordToServerTranspiler 未满足设定日志等级的数据不会被记录

  • 参数: LevelEnum

useTranspiler

  • 参数: Transpiler

removeTranspiler

  • 参数: Transpiler

clearTranspilers

dispose

释放资源,不再使用Logger时应当调用此方法来消除定时器等资源

stash

  • 参数: Metadata
  • 参数: ...any[]

info

  • 参数: Metadata
  • 参数: ...any[]

log

  • 参数: Metadata
  • 参数: ...any[]

debug

  • 参数: Metadata
  • 参数: ...any[]

warn

  • 参数: Metadata
  • 参数: ...any[]

error

  • 参数: Metadata
  • 参数: ...any[]

Metadata

日志元数据

export default class Metadata {
    /**
     * 标识各类APP
     */
    app_id: string;
    /**
     * 分类
     */
    type: TypeEnum;
    /**
     * 日志等级
     */
    level: LevelEnum;
    /**
     * 开发人员上传的日志时间戳(毫秒级)
     */
    log_time: number;
    /**
     * 页面地址
     */
    path: string;
    /**
     * 记录的信息
     */
    message: string;
    /**
     * 上下文
     */
    context: string;
    /**
     * 错误日志可能需要这个
     */
    stack?: string | undefined;
    /**
     * 用户身份标识
     */
    user_id?: string | number;
    /**
     * 系统类型。例如 ios, android等
     */
    system_type?: string | undefined;
    /**
     * 系统版本
     */
    system_version?: string | undefined;
    /**
     * 运行环境
     */
    run_env: RunEnvEnum;
    /**
     * app版本
     */
    app_version?: string | undefined;
    /**
     * H5版本
     */
    h5_version?: string | undefined;
    /**
     * 一般浏览器和webview环境是可以拿到ua的
     */
    user_agent?: string | undefined;
    /**
     * 额外标签。多个标签使用 , 分割。可被用于搜索
     */
    tags?: string | undefined;
    _rawArgs: any[];
    _uploaded: boolean;
    constructor(params?: Partial<Metadata>);
    validate(): {
        valid: boolean;
        errors: Array<{
            field: string;
            errors: string[];
        }>;
    };
}

Transpiler

真正处理日志数据的模块

/**
 * Transpiler.ts
 */
import type Logger from './Logger';
import type Metadata from './Metadata';
export default abstract class Transpiler {
    abstract name: string;
    ctx?: Logger | null;
    /**
     * Logger收到的数据都会被转发到此函数
     */
    abstract receiver(_mdata: Metadata): any;
    /**
     * 使用 logger.useTranspiler 注册后会回调此方法
     */
    installed(ctx: Logger): void;
    /**
     * 在Logger中移除会触发此方法
     */
    uninstalled(_ctx: Logger): void;
    setCtx(ctx: Logger | null): void;
    /**
     * 释放资源
     */
    dispose(): void;
}

简单的console打印功能实现

import { Transpiler } from '@yolanda-qn/logger';

class Console {
  format(mdata: Metadata): any {
    return [
      `[${formatTime(new Date(mdata.log_time), 'YYYY-MM-DD HH:mm:ss-S')}][${mdata.level}]`,
      ...mdata._rawArgs,
    ];
  }

  info(mdata: Metadata) {
    console.info(...this.format(mdata));
  }

  log(mdata: Metadata) {
    console.log(...this.format(mdata));
  }

  debug(mdata: Metadata) {
    console.debug(...this.format(mdata));
  }

  warn(mdata: Metadata) {
    console.warn(...this.format(mdata));
  }

  error(mdata: Metadata) {
    console.error(...this.format(mdata));
  }
}

export default class ConsoleTranspiler extends Transpiler {
  name = 'ConsoleTranspiler';

  constructor(private readonly console: Console = new Console()) {
    super();
  }

  receiver(mdata: Metadata) {
    this.console[mdata.level]?.(mdata);
  }
}

RecordToServerTranspiler

将日志数据发送给服务器

import { transpilers } from '@yolanda-qn/logger';

const RecordToServerTranspiler = transpiler.RecordToServer;
new RecordToServerTranspiler({
  // 服务器地址。服务器接口应该被设计为接收批量数据
  url: 'xxx',
  // 发送给服务器数据转换
  transform: (mdatas) => mdatas,
  // 请求头
  headers: {},
});

setSendDataSize

设置一次性发送的数据条数

  • 参数: number
  • 默认: 50

setStackSize

设置缓存池的大小。超过此大小则不再接收新的数据,除非数据发给了服务器。

  • 参数: number
  • 默认: 2000

setInterval

设置发送数据给服务器的时间间隔

  • 参数: number
  • 默认: 3000

useHttp

注册提供通信能力的Http类

  • 参数: typeof Http

setUrl

设置服务器接口地址

  • 参数: string

setTransform

数据转换

  • 参数: (mdatas: Metadata[]) => any

setHeaders

请求头

  • 参数: Record<string, string>

clearStack

清除缓存数据

useStorage

注册提供数据缓存能力的Storage类

  • 参数: typeof Storage

disposeHttp

移除Http

disposeStorage

移除Storage

clearStorage

清除缓存数据

abortSendToServer

暂停发送数据给服务器。一分钟后自动唤醒重新发送数据

stopSendToServer

停止发送数据给服务器

resumeSendToServer

重新开始定时发送数据给服务器

flushToServer

将数据全部发送给服务器

flushToStorage

将当前stack数据缓存到本地缓存

dispose

释放资源

Http

提供数据通信能力定义

export default abstract class Http {
    headers: Record<string, string>;
    url: string;
    method: 'get' | 'post' | 'put';
    data: any;
    transform?: (data: any) => any;
    /**
     * 为0则不设置超时时间
     */
    timeout: number;
    responseType: 'text' | 'json';
    /**
     * 如果知道数据类型一定是正确的,可以关闭此项来提升些许性能
     */
    useValidate: boolean;
    constructor(params: Partial<Pick<Http, 'headers' | 'method' | 'data' | 'transform' | 'timeout' | 'responseType'>> & Required<Pick<Http, 'url'>>);
    validate(): boolean;
    abstract send(data?: any): Promise<any>;
}
export declare class RequestError extends Error {
    xhr: XMLHttpRequest;
    status: number;
    detail: any;
    message: string;
    constructor(params: Omit<RequestError, 'name'>);
}

对应环境的实现

import { Http } from '@yolanda-qn/logger';

export default WebHttp extends Http {
  async send() {
    // 使用 XMLHttpRequest 或 fetch 去实现
  }
}

Storage

提供本地日志缓存功能

export default abstract class Storage {
    key: string;
    setKey(key: string): void;
    abstract get(): Promise<Array<Metadata>>;
    abstract set(value: Array<Metadata>): Promise<void>;
    abstract clear(): Promise<void>;
}

对应环境的实现

import { Storage, Metadata } from '@yolanda-qn/logger';

export default class WebStorage extends Storage {
  async get(): Promise<Array<Metadata>> {
    const storage = localStorage.getItem(this.key);
    if (storage) {
      const mdatas = JSON.parse(storage);
      return mdatas.map((m: any) => new Metadata(m))
        .filter((m: Metadata) => {
          const r = m.validate();
          if (r.valid) {
            return true;
          }
          console.warn('[WebStorage::get] mdata validate failed, it will be dropped.', '\nmdata', m, '\nerrors', r.errors);
          return false;
        });
    }
    return [];
  }

  async set(value: Array<Metadata>) {
    localStorage.setItem(this.key, JSON.stringify(value));
  }

  async clear() {
    localStorage.removeItem(this.key);
  }
}

枚举值

export enum LevelEnum {
  info = 'info',
  log = 'log',
  warn = 'warn',
  debug = 'debug',
  error = 'error',
}

export enum RunEnvEnum {
  unknown = 'unknown',
  /**
   * 传统浏览器环境
   */
  browser = 'browser',
  /**
   * 混合式开发
   */
  webview = 'webview',
  /**
   * 微信小程序(保留)
   */
  miniprogram = 'miniprogram',
  /**
   * 快应用(保留)
   */
  quickapp = 'quickapp',
}

export enum TypeEnum {
  /**
   * 默认未分类
   */
  default = 'default',
  /**
   * 系统级别
   */
  system = 'system',
  /**
   * app级别
   */
  app = 'app',
  /**
   * http请求
   */
  request = 'request',
  /**
   * 视图相关
   */
  view = 'view',
}