Python日志logging实战教程

一、什么是日志

在《网络安全之认识日志采集分析审计系统》中我们认识了日志。日志数据的核心就是日志消息或日志,日志消息是计算机系统、设备、软件等在某种刺激下反应生成的东西。

日志数据(log data)就是一条日志消息的内在含义,用来告诉你为什么生成日志消息的信息。日志(log)指用于展示某些事件全貌的日志消息的集合。

二、为什么要写日志

日志是对软件执行时所发生事件的一种追踪方式。软件开发人员对他们的代码添加日志调用,借此来指示某事件的发生。一个事件通过一些包含变量数据的描述信息来描述。对于软件系统来说,健全的日志记录是程序调试、故障定位、事件追溯的有效手段。

日志通用的几种类型:

  • 信息(Info):告诉用户和管理员发生了一些没有风险的事情。
  • 调试(Debug):在应用程序代码运行时生成调试信息,给软件开发人员提供故障检测和定位问题的帮助。
  • 警告(Warning):缺少需要的文件、参数、数据,但又不影响系统运行时生成警告。
  • 错误(Error):传达在计算机系统重出现的各种级别的错误。许多错误消息只能给出为什么出错的起点,要寻找出导致错误发生的根本原因还需要进一步的调查。

三、Python日志logging模块实战

在进行Python程序开发时,Python提供了logging模块,能够很好的帮助开发人员很方便的的记录日志信息。

对于简单的日志使用来说日志功能提供了一系列便利的函数。它们是 debug(),info(),warning(),error() 和 critical()。想要决定何时使用日志,请看下表,其中显示了对于每个通用任务集合来说最好的工具。

你想要执行的任务此任务的最好的工具
对于命令行或程序的应用,结果显示在控制台。print()
在对程序的普通操作发生时提交事件报告(比如:状态监控和错误调查)logging.info() 函数(当有诊断目的需要详细输出信息时使用 logging.debug() 函数)
提出一个警告信息基于一个特殊的运行时事件warnings.warn() 位于代码库中,该事件是可以避免的,需要修改客户端应用以消除告警logging.warning() 不需要修改客户端应用,但是该事件还是需要引起关注
对一个特殊的运行时事件报告错误引发异常
报告错误而不引发异常(如在长时间运行中的服务端进程的错误处理)logging.error(), logging.exception() 或 logging.critical() 分别适用于特定的错误及应用领域

日志功能应以所追踪事件级别或严重性而定。各级别适用性如下(以严重性递增):

级别何时使用
DEBUG细节信息,仅当诊断问题时适用。
INFO确认程序按预期运行。
WARNING表明有已经或即将发生的意外(例如:磁盘空间不足)。程序仍按预期进行。
ERROR由于严重的问题,程序的某些功能已经不能正常执行
CRITICAL严重的错误,表明程序已不能继续执行

默认的级别是 WARNING,意味着只会追踪该级别及以上的事件,除非更改日志配置。

所追踪事件可以以不同形式处理。最简单的方式是输出到控制台。另一种常用的方式是写入磁盘文件。

Python的logging库采用模块化方法,并提供了几类组件:记录器,处理程序,过滤器和格式化程序。

  • 记录器(Logger):提供应用程序代码直接使用的接口。
  • 处理器(Handler):将日志记录(由记录器创建)发送到适当的目的地。
  • 筛选器(Filter):提供了更细粒度的功能,用于确定要输出的日志记录。
  • 格式器(Formatter):程序在最终输出日志记录的内容格式。

logging的工作流程:以记录器Logger为对象,设置合适的处理器Handler,辅助以筛选器Filter、格式器Formatter,设置日志级别以及常用的方法,最终输出理想的日志记录给到指定目标
一个Logger可以包含多个Handler;
每个Handler可以设置自己的Filter和Formatter;
记录器和处理器中的日志事件信息流程如下图所示:
日志事件信息流程

接下来我们通过几个简单的应用场景来进行日志记录实战

1、最简单的日志记录

main.py

import logging

def print_hi(name):
    logging.debug('this is print_hi debug')
    logging.info('this is print_hi info')
    logging.warning('this is print_hi warning')
    logging.error('this is print_hi error')
    logging.critical('this is print_hi critical')
    print(f'Hi print_hi, {name}')

if __name__ == '__main__':
    print_hi('XieJava')

结果如下:
最简单的日志记录结果

这里体现了两个问题:
1.通过print()在控制台打印的日志,比logging打印的日志提前打印显示,说明日志记录是多线程的。在平时日志调试跟踪的时候注意这一点,print()的信息有时会打印在logging前有时会在logging后。

2.debug和info的日志没有打印出来,说明logging默认的日志级别是waring。
如果要设置改变默认的日志级别可以通过配置来设置日志级别如:level=logging.DEBUG

2、设置日志级别

import logging

logging.basicConfig(level=logging.DEBUG) #设置日志级别

def print_hi(name):
    logging.debug('this is print_hi debug')
    logging.info('this is print_hi info')
    logging.warning('this is print_hi warning')
    logging.error('this is print_hi error')
    logging.critical('this is print_hi critical')
    print(f'Hi print_hi, {name}')

if __name__ == '__main__':
    print_hi('XieJava')

设置日志级别结果

这下从DEBUG到CRITICAL级别的都打印出来了。

3、设置日志显示格式

默认的日志打印显示的格式是, 日志级别:logger实例名称(默认是root):日志消息内容
如这里显示的是:

DEBUG:root:this is print_hi debug
INFO:root:this is print_hi info
WARNING:root:this is print_hi warning
ERROR:root:this is print_hi error
CRITICAL:root:this is print_hi critical

在真实使用的场景下,一般都要显示日志的时间,我们可以通过设置日志显示格式来调整我们需要显示的日志格式和内容。

LOG_FORMAT = "%(asctime)s - %(levelname)s %(name)s %(filename)s [line:%(lineno)d] - %(message)s"
logging.basicConfig(format=LOG_FORMAT,level=logging.DEBUG)

这里设置了日志发生时间、日志级别、logger实例名称、日志发生的文件名、日志发生所在的行、日志消息内容。
设置日志格式

更多的logRecord属性如下:

属性名称格式描述
args此属性不需要用户进行格式化。合并到 msg 以产生 message 的包含参数的元组,或是其中的值将被用于合并的字典(当只有一个参数且其类型为字典时)。
asctime%(asctime)s表示 LogRecord 何时被创建的供人查看时间值。 默认形式为 ‘2003-07-08 16:49:45,896’ (逗号之后的数字为时间的毫秒部分)。
created%(created)fLogRecord 被创建的时间(即 time.time() 的返回值)。
exc_info此属性不需要用户进行格式化。异常元组(例如 sys.exc_info)或者如未发生异常则为 None。
filename%(filename)spathname 的文件名部分。
funcName%(funcName)s函数名包括调用日志记录。
levelname%(levelname)s消息文本记录级别(‘DEBUG’,‘INFO’,‘WARNING’,‘ERROR’,‘CRITICAL’)
levelno%(levelno)s消息数字的记录级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL)
lineno%(lineno)d发出日志记录调用所在的源行号(如果可用)。
message%(message)s记入日志的消息,即 msg % args 的结果。 这是在发起调用 Formatter.format() 时设置的。
module%(module)s模块 (filename 的名称部分)。
msecs%(msecs)dLogRecord 被创建的时间的毫秒部分。
msg此属性不需要用户进行格式化。在原始日志记录调用中传入的格式字符串。 与 args 合并以产生 message,或是一个任意对象 (参见 使用任意对象作为消息)。
name%(name)s用于记录调用的日志记录器名称。
pathname%(pathname)s发出日志记录调用的源文件的完整路径名(如果可用)。
process%(process)d进程ID(如果可用)
processName%(processName)s进程名(如果可用)
relativeCreated%(relativeCreated)d以毫秒数表示的 LogRecord 被创建的时间,即相对于 logging 模块被加载时间的差值。
stack_info此属性不需要用户进行格式化。当前线程中从堆栈底部起向上直到包括日志记录调用并引发创建当前记录堆栈帧创建的堆栈帧信息(如果可用)。
thread%(thread)d线程ID(如果可用)
threadName%(threadName)s线程名(如果可用)

4、记录日志到日志文件

logging默认是显示在控制台,在真实生产环境肯定时需要将日志记录到日志文件的。logging也是可以通过配置很方便的将日志记录到日志文件。

LOG_FORMAT = "%(asctime)s - %(levelname)s %(name)s %(filename)s [line:%(lineno)d] - %(message)s"
logging.basicConfig(filename='log.log',format=LOG_FORMAT,level=logging.DEBUG)

输出到日志文件中
输出到日志文件
输出到日志文件效果
将日志输出到日志文件,如果是日志量非常大,在实际生产环境经常碰到的是要对日志文件进行分隔,根据日志文件的大小或日期来分割生成多个日志文件。
这里介绍通过日志文件大小分割和通过日期来分割日志文件。

1.通过日志文件大小分割

RotatingFileHandler
日志记录到文件中,且支持指定日志文件大小,备份文件数量

logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)

maxBytes:日志文件大小,单位为字节
backupCount:备份文件数量

LOG_FORMAT = "%(asctime)s - %(levelname)s %(name)s %(filename)s [line:%(lineno)d] - %(message)s"
rfh=logging.handlers.RotatingFileHandler(filename='log.log',encoding='UTF-8', maxBytes=1024, backupCount=2)
logging.basicConfig(format=LOG_FORMAT,level=logging.DEBUG,handlers=[rfh])

这里设置的是当文件超过1024bytes就会对文件进行分割,备份文件数量为2,得到log.log.1、log.log.2,当log.log.2写满时又回循环写到log.log中。
根据日志文件大小循环切割生成日志文件

2.通过日期来分割日志文件

TimedRotatingFileHandler
日志记录到文件中,支持按时间间隔来更新日志

logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None)

指定的文件会被打开并用作日志记录的流。 对于轮换操作它还会设置文件名前缀。 轮换的发生是基于 when 和 interval 的积。
你可以使用 when 来指定 interval 的类型。 可能的值列表如下。 请注意它们不是大小写敏感的。

间隔类型如果/如何使用 atTime
‘S’忽略
‘M’分钟忽略
‘H’小时忽略
‘D’忽略
‘W0’-‘W6’工作日(0=星期一)用于计算初始轮换时间
‘midnight’如果未指定 atTime 则在午夜执行轮换,否则将使用 atTime。用于计算初始轮换时间
import logging
from datetime import time
from logging.handlers import RotatingFileHandler

LOG_FORMAT = "%(asctime)s - %(levelname)s %(name)s %(filename)s [line:%(lineno)d] - %(message)s"
tfh=logging.handlers.TimedRotatingFileHandler('tfh_log.log', when='S', interval=1.5, backupCount=2, encoding='UTF-8', delay=False, utc=False, atTime=time)
rfh=logging.handlers.RotatingFileHandler(filename='log.log',encoding='UTF-8', maxBytes=1024, backupCount=2)
logging.basicConfig(format=LOG_FORMAT,level=logging.DEBUG,handlers=[rfh,tfh])

为了演示方便,这里when=‘S’, interval=1.5 即1.5秒循环生成一个日志文件。在实际生产环境一般根据日志量的大小,可以配置成每天生成一个日志文件。
通过日期来分割日志文件

5、既生成日志文件又在控制台打印日志

有时候为了调试方便,还是想在控制台打印日志。能不能既生成日志文件又在控制台打印日志呢?通过配置logging的StreamHandler也是可以做到的。
StreamHandler
StreamHandler 类位于核心 logging 包,它可将日志记录输出发送到数据流例如 sys.stdout, sys.stderr 或任何文件类对象(或者更精确地说,任何支持 write() 和 flush() 方法的对象)。

sh=logging.StreamHandler()
logging.basicConfig(format=LOG_FORMAT,level=logging.DEBUG,handlers=[rfh,tfh,sh])

既生成日志文件又在控制台打印日志

6、多个模块中记录日志

在实际项目使用过程中,一个好的实践是将日志配置的模块封装好成为一个通用的日志模块组件,可以给项目中所有的模块使用。
这里我们将配置好的日志logging从main.py中抽出来形成一个logutils.py的通用模块,其他模块就可以使用了
logutils.py

import logging
from datetime import time
from logging.handlers import RotatingFileHandler

LOG_FORMAT = "%(asctime)s - %(levelname)s %(name)s %(filename)s [line:%(lineno)d] - %(message)s"
tfh=logging.handlers.TimedRotatingFileHandler('tfh_log.log', when='S', interval=1.5, backupCount=2, encoding='UTF-8', delay=False, utc=False, atTime=time)
rfh=logging.handlers.RotatingFileHandler(filename='log.log',encoding='UTF-8', maxBytes=1024, backupCount=2)
sh=logging.StreamHandler()
logging.basicConfig(format=LOG_FORMAT,level=logging.DEBUG,handlers=[rfh,tfh,sh])

公共日志模块

如在othermodule.py中使用

from logutils import logging

class TestModule():

    def print_log(self):
        logging.info('this is TestModule.print_log() info')

    @staticmethod
    def print_log_staic():
        logging.info('this is TestModule.print_log_staic info')

在main.py中使用

from logutils import logging
from othermodule import TestModule

def print_hi(name):
    logging.debug('this is print_hi debug')
    logging.info('this is print_hi info')
    logging.warning('this is print_hi warning')
    logging.error('this is print_hi error')
    logging.critical('this is print_hi critical')
    print(f'Hi print_hi, {name}')


if __name__ == '__main__':
    print_hi('XieJava')
    TestModule.print_log_staic() #类方法中打印日志
    testModule=TestModule()
    testModule.print_log()  #实例方法中打印日志

其他模块中显示日志

项目工程中所有的模块只要通过 from logutils import logging 引入logging就可以使用配置好的logging记录日志了。

7、每个不同的模块使用不同的日志记录器记录日志

现在我们在所有的模块中都是用的默认的root记录器来记录的日志,实际上也可以让每个不同的模块使用不同的日志记录器记录日志。
日志事件信息在 LogRecord 实例中的记录器、处理器、过滤器和格式器之间传递。
通过调用 Logger 类(以下称为 loggers , 记录器)的实例来执行日志记录。
在命名记录器时使用的一个好习惯是在每个使用日志记录的模块中使用模块级记录器,命名如下:

logger = logging.getLogger(__name__)

这意味着记录器名称跟踪包或模块的层次结构,并且直观地从记录器名称显示记录事件的位置。
记录器层次结构的根称为根记录器。 这是函数 debug() 、 info() 、 warning() 、 error() 和 critical() 使用的记录器,它们就是调用了根记录器的同名方法。 函数和方法具有相同的签名。 根记录器的名称在输出中打印为 ‘root’ 。
实际上我们只要通过 logger = logging.getLogger(__name__) 给每个模块定义一个记录器就可以了。
main.py

from logutils import logging
from othermodule import TestModule
logger=logging.getLogger(__name__)  #定义模块日志记录器

def print_hi(name):
    logger.debug('this is print_hi debug')
    logger.info('this is print_hi info')
    logger.warning('this is print_hi warning')
    logger.error('this is print_hi error')
    logger.critical('this is print_hi critical')
    print(f'Hi print_hi, {name}')


if __name__ == '__main__':
    print_hi('XieJava')
    TestModule.print_log_staic() #类方法中打印日志
    testModule=TestModule()
    testModule.print_log()  #实例方法中打印日志

othermodule.py

from logutils import logging
logger=logging.getLogger(__name__) #定义模块日志记录器

class TestModule():

    def print_log(self):
        logger.info('this is TestModule.print_log() info')

    @staticmethod
    def print_log_staic():
        logger.info('this is TestModule.print_log_staic info')

每个不同的模块使用不同的日志记录器记录日志
可以看到模块日志记录器打印出来的日志中模块名不再是默认的root,而是各自的模块名。

8、通过配置文件配置日志记录器

在实际项目应用的过程中,通常通过配置文件来配置日志记录器的各种配置,这样的好处就是改变日志记录的配置不需要修改代码,直接修改配置文件就可以了。
接下来介绍如何通过配置文件配置logging日志记录器
新建 logging.conf 配置文件,通过如下配置将前面代码中的处理器,日志记录器,通过配置文件的方式配置好。
logging.conf

[loggers]
keys=root,logger01

[logger_root]
level=DEBUG
handlers=sh

[logger_logger01]
level=DEBUG
handlers=sh,tfh,rfh
qualname=logger01
propagate=0

[handlers]
keys=sh,tfh,rfh

[handler_sh]
class=StreamHandler
level=DEBUG
formatter=form01
args=(sys.stderr,)

[handler_tfh]
class=handlers.TimedRotatingFileHandler
level=DEBUG
formatter=form01
args=('tfh_log.log','S',1.5,2,)

[handler_rfh]
class=handlers.RotatingFileHandler
level=DEBUG
formatter=form01
args=('log.log','a',1024,2)

[formatters]
keys=form01

[formatter_form01]
format=%(asctime)s - %(levelname)s %(name)s %(filename)s [line:%(lineno)d] - %(message)s

在logutils.py中应用配置文件

import logging.config
logging.config.fileConfig("logging.conf")

logutils.py中的代码就异常简单了,应为原来通过代码实现的配置,都写到了logging.conf配置文件中了。
配置文件实现了日志代码中的功能

这里要注意的是,在应用日志记录器的时候,需要引用配置文件中配置的记录器,如配置文件中配置了root和logger01,在应用的时候可以引用这两个记录器,当然也可以在配置文件中配置更多的记录器。
在mian.py和othermodule.py中应用日志记录器的时候,需要注意记录器用要配置文件中定义的记录器,这里是logger01。

logger=logging.getLogger('logger01')  #定义模块日志记录器

定义模块日志记录器

8、日志中中文显示

当日志信息中有中文的时候,在控制台输出会自动的转码,但有时在文件输出的时候会出现乱码。
中文信息控制台输出没有问题

控制台输出中文,但日志文件中是乱码。
日志文件中是乱码

对照python官方说明文档设置编码,设置处理器的编码为UTF-8

[handler_tfh]
class=handlers.TimedRotatingFileHandler
level=DEBUG
formatter=form01
args=('tfh_log.log','S',1.5,2,'UTF-8')

[handler_rfh]
class=handlers.RotatingFileHandler
level=DEBUG
formatter=form01
args=('log.log','a',1024,2,'UTF-8')

配置文件的参数
现在重新执行main.py,可以看到日志文件中可以正常显示中文。
日志文件显示中文

至此,我们从一个简单的日志记录实战,一步一步实现了自定义日志格式、写日志文件、抽出公共日志模块让其他模块用、同时写多个日志文件并进行日志文件切割、通过配置文件实现日志参数的定义、解决日志中文显示问题。基本覆盖了真实应用场景日志的使用。


博客:http://xiejava.ishareread.com/

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第十四届蓝桥杯第三期模拟赛 【python】

第十四届蓝桥杯第三期模拟赛 【python】 文章目录第十四届蓝桥杯第三期模拟赛 【python】✨最小的十六进制(python的16进制)❓️问题描述答案提交🧠思路🖥︎参考答案✨Excel的列(进制转化)❓️问题描述答案…

串口通信(STM32演示实现)

目录 一、串行通信的概念 二、寄存器 2.1控制寄存器USART_CR1 2.2控制寄存器USART_CR2​编辑 2.3串口寄存器USART_BRR 2.4 USART_ISR 2.5USART_TDR 2.6USART_RDR​编辑 三、实现串口数据的收发 一、串行通信的概念 u通信,最少要有两个对象,一个收…

强化学习、监督学习、无监督学习是什么

1 强化学习 1.1 定义 强化学习是机器学习学习方式的一种,是让计算机实现从一开始完全随机的进行操作,通过不断试错的方式去总结出每一步的最佳行为决策,基于环境给予的反馈,去调整自己的行为决策,从而对未来的行为给…

什么是推挽输出,开漏输出?

这篇文章是看B站“工科男孙老师”这个视频的笔记推挽 开漏 高阻 这都是谁想出来的词?? 我觉得讲的很好,做一下笔记 1.什么是IO输出三态 一共有:高电平, 低电平,浮空/高阻态 三种IO态 2.推挽输出 推挽输出能够表示高、…

短链接是怎么设计的?带你入门

文章目录前言一、短链1、原理1.1 短链生成原理1.2 短链跳转原理:2、设计:2.1 短链需求2.2 考虑的问题?二、实践案例1、设计表:2、生成短链:前言 说到 URL 你肯定不陌生,浏览器输入一段 URL,立马…

QMessageBox手动添加按钮并绑定按钮的信号

视频展示效果(结合代码看效果更佳哦,代码在最下面): QMessageBox手动添加有重试效果的按钮效果图: 点击详细文本之后展开如下图: 图标可选: QMessageBox::Critical错误图标QMessageBox::NoIco…

第二十一天 数据库开发-MySQL

目录 数据库开发-MySQL 前言 1. MySQL概述 1.1 安装 1.2 数据模型 1.3 SQL介绍 1.4 项目开发流程 2. 数据库设计-DDL 2.1 数据库操作 2.2 图形化工具 2.3 表操作 3. 数据库操作-DML 3.1 增加(insert) 3.2 修改(update) 3.3 删除(delete) 数据库开发-MySQL 前言 …

深度学习:GPT1、GPT2、GPT-3

深度学习:GPT1、GPT2、GPT3的原理与模型代码解读GPT-1IntroductionFramework自监督学习微调ExperimentGPT-2IntroductionApproachConclusionGPT-3GPT-1 Introduction GPT-1(Generative Pre-training Transformer-1)是由OpenAI于2018年发布的…

从0到1深度学习环境搭建

目录第一步:安装anaconda第二步:创建一个虚拟环境试一下第三步:确定cuda算力,配置cudapytorch官网找版本pycharm配置pycharm进行设置setting 能够打开conda的shell终端如何给下载的项目设置合适的环境如果必须要低版本的pytorch才…

智驾芯片“性价比之王”凭何抢滩增量市场?

未来几年,智能驾驶功能将进入跨越式升级的阶段,同时L2将快速普及,L2进入集中放量的阶段。 包括自动泊车 (APA)、家庭区域记忆泊车 (HAVP)、交通拥堵辅助 (TJA)、高速辅助驾驶 (HWA)、自动辅助导航驾驶 (NOA) 等在内的功能已为普通车主耳熟能…

美颜sdk的动态面具、3D面具实现流程

在美颜sdk的实现中,面具是很重要的一个部分,不管是动态面具还是3D面具都需要实现的,我们在开发中常用的是动态面具和3D面具。但是两种面具有很多不同之处,比如制作材料、制作方式等等。在这里我们先来了解一下动态面具和3D面具是如…

8个不能错过的程序员必备网站,惊艳到我了!!!

程序员是一个需要不断学习的职业,不少朋友每天来逛CSDN、掘金等网站,但一直都抱着“收藏从未停止,学习从未开始”的态度,别骗自己了兄弟。在编程体系中,有很多不错的小工具,可以极大得提升我们的开发效率。…

电容在微分、积分电路中的本质以及应用

很多朋友觉得PID是遥不可及,很神秘,很高大上的一种控制,对其控制原理也很模糊,只知晓概念性的层面,知其然不知其所以然,那么本期从另类视角来探究微分、积分电路的本质,意在帮助理解PID的控制原…

第十四届蓝桥杯三月真题刷题训练——第 21 天

目录 第 1 题:灭鼠先锋 问题描述 运行限制 代码: 思路: 第 2 题:小蓝与钥匙 问题描述 答案提交 运行限制 代码: 思路 : 第 3 题:李白打酒加强版 第 4 题:机房 第 1 题&#xff1…

存储专题扩容,HA、LB分布式存储

一、架构与存储的关系一个新的硬盘在linux系统里使用一般来说就三步:(分区,格式化)-挂载-使用blocklvs:四层负载均衡,nginx、haproxy四层和七层都有redis、memcache缓存中间件是缓存后端数据库读的信息。高端的容器技术,一旦系统出现可以可以直接重装系统…

【springboot】读写分离:

文章目录一、mysql主从复制(从库可以有多个):【1】提前准备好两台服务器,分别安装Mysql并启动成功【2】配置---主库Master【3】配置---从库Slave【4】克隆的虚拟机导致mysql主从UUID一致怎么修改:【5】测试二、读写分离…

springboot学生综合测评系统

031-springboot学生综合测评系统演示录像2022开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7(一定要5.7版本) 数据库工具:Navicat11 开发软件&…

uniapp封装各个时间方法

难点:在项目中我们经常会用到时间转换或时间比对加减问题为了方便很多页面去调用时间方法,我把时间方法封装成了公共方法1.首先在根目录创建文件夹与pages平级,我这里创建了plugins文件夹2.其次在plugins文件夹下面创建index.js文件&#xff…

LeetCode题解 20(17,79) 电话号码的字母组合,单词搜索<回溯>

文章目录电话号码的字母组合(17)代码解答单词搜索(79)代码解答电话号码的字母组合(17) 思路: 根据题意我们必须根据数字获取对应的字符数组,因此我们先定义1个字符数组表示这个电话表 private String[] letters {"","","abc","…

C语言例程:学生成绩管理程序

学生成绩管理程序 实例说明 编制一个统计存储在文件中的学生考试分数的管理程序。设学生成绩以一个学生一条记录的 形式存储在文件中,每个学生记录包含的信息有姓名、学号和各门功课的成绩。要求编制具有以 下几项功能的程序:求出各门课程的总分&#…
最新文章