【NestJs】日志收集

news2024/11/23 19:38:45

Nest 附带一个默认的内部日志记录器实现,它在实例化过程中以及在一些不同的情况下使用,比如发生异常等等(例如系统记录)。这由 @nestjs/common 包中的 Logger 类实现。你可以全面控制如下的日志系统的行为:

  • 完全禁用日志
  • 指定日志系统详细水平(例如,展示错误,警告,调试信息等)
  • 覆盖默认日志记录器的时间戳(例如使用 ISO8601 标准作为日期格式)
  • 完全覆盖默认日志记录器
  • 通过扩展自定义默认日志记录器
  • 使用依赖注入来简化编写和测试你的应用
  • 你也可以使用内置日志记录器,或者创建你自己的应用来记录你自己应用水平的事件和消息。

更多高级的日志功能,可以使用任何 Node.js 日志包,比如Winston,来生成一个完全自定义的生产环境水平的日志系统。

重点目录

  • 常见日志及获取(记录)方式
  • 第三方日志方案:winston(勤快的人)、pino(推荐懒人)
  • 通用业务系统日志系统配置(学习定时任务)

日志等级

  • Log : 通用日志,按需进行记录(打印)
  • Warning:警告日志,比如: 尝试多次进行数据库操作
  • Error:产重日志,比如:数据库异常
  • Debug: 调试日志,比如:加载数据日志
  • Verbose:详细日志,所有的操作与详细信息(非必要不打印)

功能分类日子

  • 错误日志->方便定位问题,给用户友好的提示
  • 调试日志->方便开发
  • 请求日志->记录敏感行为

日志记录位置

  • 控制台日志->方便监看(调试用)
  • 文件日志->方便回溯与追踪(24小时滚动)
  • 数据库日志->敏感操作、敏感数据记录

Nestjs 中记录日志

在这里插入图片描述
接下来我们实操一下日志功能。

基础自定义

要禁用日志,在(可选的)Nest 应用选项对象中向 NestFactory.create() 传递第二个参数设置 logger 属性为 false 。
app.module.ts

const app = await NestFactory.create(ApplicationModule, {
  logger: false,
});
await app.listen(3000);

根据级别显示

// 层次类型
export type LogLevel = 'log' | 'error' | 'warn' | 'debug' | 'verbose';

// 创建app时使用配置 logger 层次
const app = await NestFactory.create(ApplicationModule, {
  logger: ['error', 'warn'],
});
await app.listen(3000);

自定Logger

Pino、日志滚动pino-roll

git地址
安装

pnpm install nestjs-pino

使用
user.module.ts注册

import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { Logs } from '../logs/logs.entity';
import { LoggerModule } from 'nestjs-pino';

@Module({
  imports: [TypeOrmModule.forFeature([User, Logs]), LoggerModule.forRoot()],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

user.controller.ts中使用

import { Controller, Delete, Get, Patch, Post } from '@nestjs/common';
import { UserService } from './user.service';
import { ConfigService } from '@nestjs/config';
import { User } from './user.entity';
import { Logger } from 'nestjs-pino';

@Controller('user')
export class UserController {
  // private logger = new Logger(UserController.name);

  constructor(
    private userService: UserService,
    private configService: ConfigService,
    private logger: Logger,
  ) {
    this.logger.log('UserController init');
  }

  @Get()
  getUsers(): any {
    // this.logger.log(`请求getUsers成功`);
    return this.userService.findAll();
    // return this.userService.getUsers();
  }

}

结果
在这里插入图片描述
在这里插入图片描述

注意 因为pino是懒人必备,所以默认打印出来的样式比较丑,那么我们还需要另外一个插件pnpm i pino-pretty 。安装完毕我们需要在app.modules.ts 做如下配置

import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { Logs } from '../logs/logs.entity';
import { LoggerModule } from 'nestjs-pino';

@Module({
  imports: [
    TypeOrmModule.forFeature([User, Logs]),
    LoggerModule.forRoot({
      pinoHttp: {
        transport: {
          target: 'pino-pretty',
          options: {
            colorize: true,
          },
        },
      },
    }),
  ],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

重启项目 日志如下,
注意 代码我们还需要改一下,因为我们在生产环境是不需要这样打印。

import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { Logs } from '../logs/logs.entity';
import { LoggerModule } from 'nestjs-pino';

@Module({
  imports: [
    TypeOrmModule.forFeature([User, Logs]),
    LoggerModule.forRoot({
      pinoHttp: {
        transport:
          process.env.NODE_ENV === 'development'
            ? {
                target: 'pino-pretty',
                options: {
                  colorize: true,
                },
              }
            : {
                target: 'pino-roll',
                options: {
                  file: 'log.txt',
                  // 周期
                  frequency: 'daily',
                  mkdir: true,
                },
              },
      },
    }),
  ],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

在这里插入图片描述
当然为了测试方便 我们可以这样写:

import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { Logs } from '../logs/logs.entity';
import { LoggerModule } from 'nestjs-pino';
import { join } from 'path';

@Module({
  imports: [
    TypeOrmModule.forFeature([User, Logs]),
    LoggerModule.forRoot({
      pinoHttp: {
        transport: {
          targets: [
            {
              level: 'info',
              target: 'pino-pretty',
              options: {
                colorize: true,
              },
            },
            {
              level: 'info',
              target: 'pino-roll',
              options: {
                file: join('logs', 'log.txt'),
                frequency: 'daily',
                mkdir: true,
              },
            },
          ],
        },
      },
      // pinoHttp: {
      //   transport:
      //     process.env.NODE_ENV === 'development'
      //       ? {
      //           target: 'pino-pretty',
      //           options: {
      //             colorize: true,
      //           },
      //         }
      //       : {
      //           target: 'pino-roll',
      //           options: {
      //             file: 'log.txt',
      //             // 周期
      //             frequency: 'daily',
      //             mkdir: true,
      //           },
      //         },
      // },
    }),
  ],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

在这里插入图片描述
当然 我们也可以放在app.modules.ts中使用

import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as dotenv from 'dotenv';
import * as Joi from 'joi';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { LoggerModule } from 'nestjs-pino';
import { join } from 'path';

import { ConfigEnum } from './enum/config.enum';

import { User } from './user/user.entity';
import { Profile } from './user/profile.entity';
import { Logs } from './logs/logs.entity';
import { Roles } from './roles/roles.entity';

const envFilePath = `.env.${process.env.NODE_ENV || `development`}`;

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath,
      load: [() => dotenv.config({ path: '.env' })],
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
          .valid('development', 'production')
          .default('development'),
        DB_PORT: Joi.number().default(3306),
        DB_HOST: Joi.string().ip(),
        DB_TYPE: Joi.string().valid('mysql', 'postgres'),
        DB_DATABASE: Joi.string().required(),
        DB_USERNAME: Joi.string().required(),
        DB_PASSWORD: Joi.string().required(),
        DB_SYNC: Joi.boolean().default(false),
      }),
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) =>
        ({
          type: configService.get(ConfigEnum.DB_TYPE),
          host: configService.get(ConfigEnum.DB_HOST),
          port: configService.get(ConfigEnum.DB_PORT),
          username: configService.get(ConfigEnum.DB_USERNAME),
          password: configService.get(ConfigEnum.DB_PASSWORD),
          database: configService.get(ConfigEnum.DB_DATABASE),
          entities: [User, Profile, Logs, Roles],
          // 同步本地的schema与数据库 -> 初始化的时候去使用
          synchronize: configService.get(ConfigEnum.DB_SYNC),
          // logging: process.env.NODE_ENV === 'development',
          logging: false,
        } as TypeOrmModuleOptions),
    }),
    LoggerModule.forRoot({
      pinoHttp: {
        transport: {
          targets: [
            process.env.NODE_ENV === 'development'
              ? {
                  level: 'info',
                  target: 'pino-pretty',
                  options: {
                    colorize: true,
                  },
                }
              : {
                  level: 'info',
                  target: 'pino-roll',
                  options: {
                    file: join('logs', 'log.txt'),
                    frequency: 'daily', // hourly
                    size: '10m',
                    mkdir: true,
                  },
                },
          ],
        },
    UserModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

winston

Winston 是强大、灵活的 Node.js 开源日志库之一,理论上, Winston 是一个可以记录所有信息的记录器。这是一个高度直观的工具,易于定制。可以通过更改几行代码来调整其背后的逻辑。它使对数据库或文件等持久存储位置的日志记录变得简单容易。

Winston 提供以下功能:

集中控制日志记录的方式和时间:在一个地方更改代码即可
控制日志发送的位置:将日志同步保存到多个目的地(如Elasticsearch、MongoDB、Postgres等)。
自定义日志格式:带有时间戳、颜色日志级别、JSON格式等前缀。

winston实践

这里我们使用的nestjs项目所以我们需要下载的是 nest-wnston 官方实例

npm install --save nest-winston winston

Replacing the Nest logger
该模块还提供了WinstonLogger类(LoggerService接口的自定义实现),供Nest用于系统日志记录。这将确保Nest系统日志和你的应用程序事件/消息日志的行为和格式的一致性。

来到我们的代码中main.ts

// import { Logger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { createLogger } from 'winston';
import { AppModule } from './app.module';
import * as winston from 'winston';

async function bootstrap() {
  // const logger = new Logger();
  // createLogger of Winston
  const instance = createLogger({
    // options of Winston
    transports: [
      new winston.transports.Console({
        level: 'info',
        // 字符串拼接
        format: winston.format.combine(
          winston.format.timestamp(),
          utilities.format.nestLike(),
        ),
      }),
    ],
  });
  const app = await NestFactory.create(AppModule, {
    // 关闭整个nestjs日志
    // logger: false,
    // logger: ['error', 'warn'],
    // bufferLogs: true,
    logger: WinstonModule.createLogger({
      instance,
    }),
  });
  app.setGlobalPrefix('api/v1');

  const port = 3000;
  await app.listen(port);

  // logger.log(`App 运行在:${port}`);
  // logger.warn(`App 运行在:${port}`);
  // logger.error(`App 运行在:${port}`);
}
bootstrap();

在来到app.module.ts文件中

// 全局注册
@Global()
@Module({
// 依赖注入
  providers: [Logger],
  // 官方文档中没有这个,有可能在按照官方实例会报错,通过官方的issues 可以看到这个问题,根据官方实例即可获得答案
  exports: [Logger],
  })
export class AppModule {}

exports 使用导出 全局都可以使用

user.controll.ts 使用

import { Controller, Delete, Get, Patch, Post, Logger } from '@nestjs/common';
import { UserService } from './user.service';
import { ConfigService } from '@nestjs/config';
import { User } from './user.entity';

@Controller('user')
export class UserController {
  // private logger = new Logger(UserController.name);

  constructor(
    private userService: UserService,
    private configService: ConfigService,
    private readonly logger: Logger,
  ) {
    this.logger.log('UserController init');
  }

  @Get()
  getUsers(): any {
    this.logger.log(`请求getUsers成功`);
    this.logger.warn(`请求getUsers成功`);
    this.logger.error(`请求getUsers成功`);
    return this.userService.findAll();
    // return this.userService.getUsers();
  }

  @Post()
  addUser(): any {
    // todo 解析Body参数
    const user = { username: 'toimc', password: '123456' } as User;
    // return this.userService.addUser();
    return this.userService.create(user);
  }
  @Patch()
  updateUser(): any {
    // todo 传递参数id
    // todo 异常处理
    const user = { username: 'newname' } as User;
    return this.userService.update(1, user);
  }

  @Delete()
  deleteUser(): any {
    // todo 传递参数id
    return this.userService.remove(1);
  }

  @Get('/profile')
  getUserProfile(): any {
    return this.userService.findProfile(2);
  }

  @Get('/logs')
  getUserLogs(): any {
    return this.userService.findUserLogs(2);
  }

  @Get('/logsByGroup')
  async getLogsByGroup(): Promise<any> {
    const res = await this.userService.findLogsByGroup(2);
    // return res.map((o) => ({
    //   result: o.result,
    //   count: o.count,
    // }));
    return res;
  }
}

打印结果:
在这里插入图片描述
滚动打印日志:
main.ts 中 使用import 'winston-daily-rotate-file';

// import { Logger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { createLogger } from 'winston';
import { AppModule } from './app.module';
import * as winston from 'winston';
import { utilities, WinstonModule } from 'nest-winston';
import 'winston-daily-rotate-file';

async function bootstrap() {
  // const logger = new Logger();
  // createLogger of Winston
  const instance = createLogger({
    // options of Winston
    transports: [
      new winston.transports.Console({
        level: 'info',
        format: winston.format.combine(
          winston.format.timestamp(),
          utilities.format.nestLike(),
        ),
      }),
      // events - archive rotate
      new winston.transports.DailyRotateFile({
        level: 'warn',
        dirname: 'logs',
        filename: 'application-%DATE%.log',
        datePattern: 'YYYY-MM-DD-HH',
        zippedArchive: true,
        maxSize: '20m',
        maxFiles: '14d',
        format: winston.format.combine(
          winston.format.timestamp(),
          winston.format.simple(),
        ),
      }),
      new winston.transports.DailyRotateFile({
        level: 'info',
      
        dirname: 'logs',
        filename: 'info-%DATE%.log',
        datePattern: 'YYYY-MM-DD-HH',
        zippedArchive: true,
        // 文件大小
        maxSize: '20m',
        // 最多14 天
        maxFiles: '14d',
        format: winston.format.combine(
          winston.format.timestamp(),
          winston.format.simple(),
        ),
      }),
    ],
  });
  const app = await NestFactory.create(AppModule, {
    // 关闭整个nestjs日志
    // logger: false,
    // logger: ['error', 'warn'],
    // bufferLogs: true,
    logger: WinstonModule.createLogger({
      instance,
    }),
  });
  app.setGlobalPrefix('api/v1');

  const port = 3000;
  await app.listen(port);

  // logger.log(`App 运行在:${port}`);
  // logger.warn(`App 运行在:${port}`);
  // logger.error(`App 运行在:${port}`);
}
bootstrap();

在这里插入图片描述
到这里我们就完成了 日志配置的三种方式:官方、 pion、winston,个人还是喜欢winston,这个还是根据项目来做决定。接下来是这篇文章的加餐课,如需要的了解更多的小伙伴可以多学习一下。

配置winston记录日志(全局异常过滤器)

场景:我们开发了很多接口,项目中我们会使用try…catch ,但是每个路由都加上的话会很麻烦,我们需要全局处理 try… catch… ,nestj 自带会帮我们处理。
在这里插入图片描述
postmen 测试一下,路由不存在的情况:
在这里插入图片描述
异常过滤器
内置的异常层负责处理整个应用程序中的所有抛出的异常。当捕获到未处理的异常时,最终用户将收到友好的响应。
在这里插入图片描述
开箱即用,此操作由内置的全局异常过滤器执行,该过滤器处理类型 HttpException(及其子类)的异常。每个发生的异常都由全局异常过滤器处理, 当这个异常无法被识别时 (既不是 HttpException 也不是继承的类 HttpException ) , 用户将收到以下 JSON 响应:

{
    "statusCode": 500,
    "message": "Internal server error"
}

项目中使用:
app.control.ts

@Get()
  getUsers(): any {
    const user = { isAdmin: false };
    if (!user.isAdmin) {
      throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
    }
    return this.userService.findAll();
    // return this.userService.getUsers();
  }

在这里插入图片描述
常见的状态码:
在这里插入图片描述
HttpException 构造函数有两个必要的参数来决定响应:

  • response 参数定义 JSON 响应体。它可以是 string 或 object,如下所述。

  • status参数定义HTTP状态代码。

默认情况下,JSON 响应主体包含两个属性:

  • statusCode:默认为 status 参数中提供的 HTTP 状态代码

  • message:基于状态的 HTTP 错误的简短描述

仅覆盖 JSON 响应主体的消息部分,请在 response参数中提供一个 string。

要覆盖整个 JSON 响应主体,请在response 参数中传递一个object。 Nest将序列化对象,并将其作为JSON 响应返回。

第二个构造函数参数-status-是有效的 HTTP 状态代码。 最佳实践是使用从@nestjs/common导入的 HttpStatus枚举。

在这里插入图片描述
基础案列演示完毕,我们开始捕获全局http协议:
新建文件夹,src\filters\http-exception.filter.ts

import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    // 获取上下文
    const ctx = host.switchToHttp();
    // 响应和请求对象
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    const status = exception.getStatus();
    response.status(status).json({
      code: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      method: request.method,
    });
  }
}

应用过滤器

在这里插入图片描述

在这里插入图片描述
基本过滤器演示结束,我们还需要在因在日志中,需要对 filters文件夹进行改造:

import { LoggerService } from '@nestjs/common';
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  constructor(private logger: LoggerService) {}
  catch(exception: HttpException, host: ArgumentsHost) {
    // 获取上下文
    const ctx = host.switchToHttp();
    // 响应和请求对象
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    const status = exception.getStatus();
    this.logger.error(exception.message, exception.stack);
    response.status(status).json({
      code: status,
      timestamp: new Date().toISOString(),
      method: request.method,
      message: exception.message || exception.name,
    });
  }
}

main.ts 改造:

// import { Logger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { createLogger } from 'winston';
import { AppModule } from './app.module';
import * as winston from 'winston';
import { utilities, WinstonModule } from 'nest-winston';
import 'winston-daily-rotate-file';
import { HttpExceptionFilter } from './filters/http-exception.filter';

async function bootstrap() {
  // const logger = new Logger();
  // createLogger of Winston
  const instance = createLogger({
    // options of Winston
    transports: [
      new winston.transports.Console({
        level: 'info',
        format: winston.format.combine(
          winston.format.timestamp(),
          utilities.format.nestLike(),
        ),
      }),
      // events - archive rotate
      new winston.transports.DailyRotateFile({
        level: 'warn',
        dirname: 'logs',
        filename: 'application-%DATE%.log',
        datePattern: 'YYYY-MM-DD-HH',
        zippedArchive: true,
        maxSize: '20m',
        maxFiles: '14d',
        format: winston.format.combine(
          winston.format.timestamp(),
          winston.format.simple(),
        ),
      }),
      new winston.transports.DailyRotateFile({
        level: 'info',
        dirname: 'logs',
        filename: 'info-%DATE%.log',
        datePattern: 'YYYY-MM-DD-HH',
        zippedArchive: true,
        maxSize: '20m',
        maxFiles: '14d',
        format: winston.format.combine(
          winston.format.timestamp(),
          winston.format.simple(),
        ),
      }),
    ],
  });
  const logger = WinstonModule.createLogger({
    instance,
  });
  const app = await NestFactory.create(AppModule, {
    logger,
  });
  app.setGlobalPrefix('api/v1');
  app.useGlobalFilters(new HttpExceptionFilter(logger));
  const port = 3000;
  await app.listen(port);

}
bootstrap();

拓展:全局所有异常捕获

import {
  ExceptionFilter,
  HttpAdapterHost,
  HttpException,
  HttpStatus,
  LoggerService,
} from '@nestjs/common';
import { ArgumentsHost, Catch } from '@nestjs/common';

import * as requestIp from 'request-ip';

// 捕获所有异常
@Catch()
export class AllExceptionFilter implements ExceptionFilter {
  constructor(
    private readonly logger: LoggerService,
    private readonly httpAdapterHost: HttpAdapterHost,
  ) {}
  catch(exception: unknown, host: ArgumentsHost) {
    const { httpAdapter } = this.httpAdapterHost;
    const ctx = host.switchToHttp();
    const request = ctx.getRequest();
    const response = ctx.getResponse();

    const httpStatus =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const responseBody = {
      headers: request.headers,
      query: request.query,
      body: request.body,
      params: request.params,
      timestamp: new Date().toISOString(),
      // 还可以加入一些用户信息
      // IP信息
      ip: requestIp.getClientIp(request),
      exceptioin: exception['name'],
      error: exception['response'] || 'Internal Server Error',
    };

    this.logger.error('[toimc]', responseBody);
    httpAdapter.reply(response, responseBody, httpStatus);
  }
}

main.ts

const httpAdapter = app.get(HttpAdapterHost);
  app.useGlobalFilters(new AllExceptionFilter(logger, httpAdapter));

ok,这是一篇长篇文章,能看到的结束的小伙伴也是很厉害的。我也是要写吐了在这里插入图片描述
写着写着就多了,可能太想和大家分享知识了。 已经学明白的同学可以 看我的下一篇文章 【NestJs】日志模块重构 这篇文章纯代码,然后使用了 开发规范创建的日志模块。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/432640.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jenkins windows安装 部署项目 前端 后端

安装 需要安装的程序&#xff1a; 1.下载jenkins windows版本 2.400 此版本需要jdk11 https://www.jenkins.io/ 按着提示安装即可 2.下载jdk 11 https://login.oracle.com/ 按着提示安装即可 部署pc 1.新建项目 2.源码管理 3.添加git用户 4.Build Steps 构建 初始化np…

vue2数据响应式原理(2)搭建webpack认识一下Object.defineProperty

在1中我们讲到 Object.defineProperty() 是vue2实现数据响应的关键 那么我们就来好好的看看这个方法 方法字面意思是定义属性 而他是通过Object对象调用的 所以说 他是用来控制对象的某个属性的 比较官方的解释是 object.defineProperty() 方法会直接在一个对象上定义一个新属…

单片机添加版本号的一些小技巧

平时我们写程序&#xff0c;通常都会备注软件版本&#xff0c;那么&#xff0c;怎么在单片机中保存版本信息呢&#xff1f; 方法其实有很多&#xff0c;但基本原理都是在指定存储区域&#xff08;Flash&#xff09;中写入软件版本信息。 实现方法 下面就分享一个最常用&#xf…

算法风险防控

算法风险防控是指在算法应用过程中&#xff0c;通过对算法应用场景、数据、模型和结果等多个方面的风险进行评估和控制&#xff0c;以保障算法应用的安全性、可靠性和合法性。以下是一些常见的算法风险防控措施&#xff1a; 数据风险防控&#xff1a;在算法应用中&#xff0c;…

【python】Python基础入门:从变量到异常处理

天池实验室代码链接&#xff1a;https://tianchi.aliyun.com/notebook-ai/home#notebookLabId491001 简介 Python 是一种通用编程语言&#xff0c;其在科学计算和机器学习领域具有广泛的应用。如果我们打算利用 Python 来执行机器学习&#xff0c;那么对 Python 有一些基本的了…

51单片机定时器与计数器

文章目录 51单片机定时器与计数器一、定时器与计数器的结构与功能计数功能定时功能 二、定时器与计数器的控制TMOD 工作方式寄存器TCON 定时器控制寄存器 三、仿真案例(一).8个LED 1 秒周期闪烁。(二) 产品包装生产线。 51单片机定时器与计数器 一、定时器与计数器的结构与功能…

ESP32设备驱动-BMP388气压传感器驱动

BMP388气压传感器驱动 文章目录 BMP388气压传感器驱动1、BMP388介绍2、硬件准备3、软件准备4、驱动实现1、BMP388介绍 BMP388 是一款非常小巧、低功耗和低噪声的 24 位绝对气压传感器。 它可以实现精确的高度跟踪,特别适合无人机应用。 BMP388 在 0-65C 之间的同类最佳 TCO,…

港联证券|AI概念板块无死角杀跌,主题炒作熄火后资金会流向哪些板块?

ChatGPT概念指数大跌7%&#xff0c;单日跌幅创历史之最。 4月10日&#xff0c;炒作逾月的ChatGPT概念板块团体大跌&#xff0c;云从科技&#xff08;688327.SH&#xff09;、三六零&#xff08;601360.SH&#xff09;、科大讯飞&#xff08;002230.SZ&#xff09;等热门股跌停&…

集中式版本控制工具 —— SVN

一、简介 1️⃣ SVN 是什么&#xff1f; 代码版本管理工具他能记住每次的修改查看所有的修改记录恢复到任何历史版本恢复已经删除的文件 2️⃣ SVN 与 Git 相比有什么优势&#xff1f; 使用简单、上手快目录级权限控制&#xff0c;企业安全必备子目录 Checkout&#xff0c;…

RK3568平台开发系列讲解(Linux系统篇)文件系统的读写

🚀返回专栏总目录 文章目录 一、文件IO1.1、文件 IO read()1.2、文件 IO write()二、系统调用层和虚拟文件系统层三、ext4 文件系统层沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们一起学习 read 和 write 调用过程。 一、文件IO 1.1、文件 IO read() rea…

openLdap2.4.44的安装部署

openLdap2.4.44的安装部署 一、安装 1.从yum源拉取 yum install -y openldap openldap-clients openldap-servers 2.复制DB到指定目录 cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG 3.给目录授权 &#xff08;如果没有ldap &#xff0c;可…

定时任务框架快速入门

一、Quartz 1. Quartz 概述 Quartz 是一个开源的作业调度框架(job scheduler)&#xff0c;几乎可以集成到任何 Java 应用程序中&#xff0c;从最小的独立应用程序到最大的电子商务系统。Quartz 可用于创建简单或复杂的调度来执行数十个、数百个甚至数万个作业&#xff1b;其任务…

[NOIP1999 普及组] Cantor 表

[NOIP1999 普及组] Cantor 表 题目描述: 现代数学的著名证明之一是 Georg Cantor 证明了有理数是可枚举的。他是用下面这一张表来证明这一命题的&#xff1a; 1/1 , 1/2 , 1/3 , 1/4, 1/5, … 2/1, 2/2 , 2/3, 2/4, … 3/1 , 3/2, 3/3, … 4…

win11下载配置Python环境+pycharm下载

前两天快乐的把我重装的win10升级成win11&#xff0c;升级的时候超怕不能成功&#xff0c;但效果还不错&#xff0c;然后突然想学一学Python&#xff0c;所以首先来配置环境吧 一、下载安装包 建议去官网&#xff0c;因为自从有了Python3之后&#xff0c;Python2就慢慢的被淘汰…

测试市场已经饱和了吗?现在转行软件测试会不会太迟?

非常有意思的话题&#xff0c;某种程度上来说&#xff0c;测试职场一条从未设想过的道路真的走通了。 这条路指广大测试呼吁对测试从业进行学历保护、专业保护&#xff0c;就像医学那样设置护城河&#xff0c;以一种令人意想不到的方式完成了。 得益于大量培训机构为了赚钱&a…

R语言地理加权回归、主成份分析、判别分析等空间异质性数据分析

在自然和社会科学领域有大量与地理或空间有关的数据&#xff0c;这一类数据一般具有严重的空间异质性&#xff0c;而通常的统计学方法并不能处理空间异质性&#xff0c;因而对此类型的数据无能为力。以地理加权回归为基础的一系列方法&#xff1a;经典地理加权回归&#xff0c;…

有趣的Hack-A-Sat黑掉卫星挑战赛——被破坏的阿波罗计算机(解法二)

国家太空安全是国家安全在空间领域的表现。随着太空技术在政治、经济、军事、文化等各个领域的应用不断增加&#xff0c;太空已经成为国家赖以生存与发展的命脉之一&#xff0c;凝聚着巨大的国家利益&#xff0c;太空安全的重要性日益凸显[1]。而在信息化时代&#xff0c;太空安…

全网最火爆,Web自动化测试POM模式分层实战,框架封装看这一篇就够了

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 线性脚本以快递 100…

【可信平台】SO202303191426,销售订单未同步,销售出库单未同步

1.销售订单未同步 这里先说下推送逻辑:1.物料描述带有‘再生’ 。2.公司是芮邦 这个物料应该在EOS里查,可我不会,我在MES里查,的确符合条件。 可信平台同步逻辑,是销售订单同步了,才会同步销售出库单。 这个SO销售订单就没同步,要先解决这个问题。 可以在接口看日志…

HighTec编译器错误记录

目录 1、HighTec安装后缺少Universal Debug Engine 2、HighTec工程改名后不能跳转函数定义&#xff0c;提示找不到定义。 3、HighTec工程重复编译 1、HighTec安装后缺少Universal Debug Engine 在HighTec安装后&#xff0c;没有调试UDE&#xff0c;重装系统后还是没有&#x…