curl 命令是一个利用 URL 规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称 curl 为下载工具。作为一款强力工具,curl 支持包括 HTTP、HTTPS、ftp 等众多协议,还支持 POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。
下载单个网址,默认将输出打印到标准输出
curl www.google.com
如果需要将页面源码保存到本地,可以使用-o
参数:
-o 将文件保存到 -o 指定的文件名
-O 将文件保存到默认文件名
curl -o google.html www.google.com
默认情况下 CURL 不会发送 HTTP Location headers(重定向). 当一个被请求页面移动到另一个站点时,会发送一个 HTTP Loaction header 作为请求,然后将请求重定向到新的地址上。
curl -L google.com
通过使用-C
选项可对大文件使用断点续传功能
curl -sSL http://to.sh | bash
使用 -i
或者 --include
参数查看返回 Header
curl -i google.com
使用 -i
参数,页面相应头 header 和页面相应 body 一起返回,如果只想查看 header,可以使用 -I
或者 --head
GET 提交直接将参数添加到 URL 后
POST 提交时
curl -X POST --data 'keyword=value' https://httpbin.org/post
其他 HTTP 方法通过 -X
参数指定即可
curl -X DELETE url
curl -X PUT --data 'key=value' url
curl -T file.txt url
curl -E mycert.pem https://url
curl -H ‘Content-Type:application/json' -H 'Authorization: bearer valid_token' URL
-c
参数保存请求返回 Cookie,本地存储文件
curl -b cook_file.txt -c response_cookie.txt URL
通过 -T
选项可指定本地文件上传到 FTP 服务器
curl -u ftpuser:ftppassword -T file.txt ftp://ftp.server
curl -u ftpuser:ftppassword -T "{file1, file2}" ftp://ftp.server
总的来说,curl 的用法比较普通,最常见的也就是用来下载文件,或者直接查看 Header,还有在命令行下发送 GET 或者 POST 请求,其他用法倒也是有,不过日常并没有经常使用到。
wget 是一个下载文件的工具,它用在命令行下。对于 Linux 用户是必不可少的工具,我们经常要下载一些软件或从远程服务器恢复备份到本地服务器。wget 支持 HTTP,HTTPS 和 FTP 协议,可以使用 HTTP 代理。
wget https://www.google.com
Axel 是 Linux 下一款不错的 HTTP 或 FTP 高速下载工具。支持多线程下载、断点续传,且可以从多个地址或者从一个地址的多个连接来下载同一个文件,适合网速不给力时多线程下载以提高下载速度。
使用 10 个线程同时下载文件
axel -n 10 url
多线程版本 wget,同时使用 5 个线程下载
mwget -n 5 url
新工具 Trello,说新工具也好,新的规划板也好,Trello 已经代替了我制定规划的其他应用,我曾经尝试过无数的时间管理项目管理工具,没有一个让我有使用 Trello 的舒畅感。
Trello 自己的介绍说自己是一个基于网页端的项目管理应用。但在我个人看来,作为个人的 Board,他是一个极好的 GTD 管理工具,如果是多人使用,那就是一个很好的协作管理工具。
怎么用其实是一个非常个人的习惯问题,他作为一个效率工具提供了各种可能,不仅能够作为简单的待办事项管理工具,甚至能够在十几或者几十人的团队中担任项目管理的工具。
这里就要说到 Trello 中的几个概念,看板、列表、卡片。幸而 Trello 的官方介绍非常详细,连中文译名也是朗朗上口,大赞。如果想要了解 Trello 如何得此名字可以看这篇文章。
一个 Trello 看板是装满卡片的各种列表的列表,它属于一个团队或你本人。可以把他看成是一个黑板,或者一个 Working Area,或者叫 Workspace。
Trello 的列表是承载卡片的容器,不同的工作团队可以根据自己的 Workflow 来定制自己的列表,如果个人作为一个 GTD 的工具,使用官方推荐的,建立三个列表 —-Todo,Doing,Done,也是不错的。
最后 Trello 的卡片,是 Trello 的核心,卡片可以看做是一项待办事项,也可以看做是一个任务,甚至可以看成一个提醒,更或者可以在卡片内部定义自己的更加具体的待办事项(Checklist)。
卡片承载的内容可以很简单,也可以非常复杂,卡片中不仅包括标题,还包括任务描述,附件,清单(Checklist),评论,可以对卡片添加标签,添加到期时间,也可以添加其他成员,甚至可以订阅该卡片的通知。一个卡片承载了非常多的功能,但是使用起来就可以根据不同的应用场景来调整。而这篇文章更是直接使用 Markdown 在卡片描述中完成的。
更加详细的内容可以看:https://trello.com/tour
Trello 团队使用 150 多个看板来管理产品进度 1
Project management apps turn ambitious ideas into workable plans.
跨平台已经成为了我选用工具首选的因素,Web,Android,iOS, 是必须要支持的,其次三大桌面平台,如果有支持当然好,没有客户端,Web App 已经要足够精致和易用。垃圾的 Evernote 就是 Web App 有些烂,还不提供 Linux 版。
想当年 Chrome 出来的时候,主打的第一个特性就是快,对于一个入口应用,必须要足够的快,使用过程中不能有感知的卡顿,否则就失去了这个工具的意义,工具都是为了提高效率而诞生的,自身不能成为影响效率的因素。幸而使用 Trello 的过程中,他也一如他宣传的那样,几乎不用刷新,实时同步,毫无卡顿。
Trello 作为一款看板应用已经被数以千计的公司使用,也得到了无数的三方应用的支持,虽然非企业版可支持的扩展 (Power up)限制为一个,但个人使用 Board,List,Card 等基本功能都是毫无限制的,可以通过 Zapier 或者 IFTTT 集成更多的自动化的工作流进来,比如在 Google Calendar 上新建一个日历时,自动在 List 中创建一个 Card,在 Card 中新建一篇内容时自动创建一个 Evernote 笔记等等,因为开放了 API,无数的可能性,都会诞生。
邮件之于 Gmail,那么卡片就之于 Trello,每一封邮件可能有自己的标签,Trello 中的卡片也有标签,默认提供了很多不同颜色的标签,用来自定义卡片紧急程度也好,定义卡片类型也好,也提供了很多可能。Trello 还提供了 Sticker 这样有趣的功能,可以将不同的 Sticker 贴到 卡片上,很有趣。
Trello 作为一个项目管理或者时间管理的工具,提醒是必不可少的,Trello 的通知方式有很多,应用中通知,邮件通知,浏览器桌面通知,移动设备推送通知,所有的通知在所有的设备上都保持同步。
Trello 更加有效的是订阅模式,如果只对某一个 任务(Card)感兴趣,可以只关心该卡片的变动通知。
如果你还不知道怎么开始的话,可以简单的看看这个
http://help.trello.com/category/694-category
在看板中双击就能在当前位置新建 List,输入名字就能够快速建立 List
如果在网站上浏览到一个图片,可以右键选择复制图片,然后在 Trello 的卡片中可以使用 Ctrl+V 来插入图片。或者如果你更愿意使用拖拽,也可以直接将网页中的图片拖拽到卡片中。
Trelle 的卡片功能丰富,你可以拿卡片做任何想做的事情,但是别忘了你还可以插入一个 Checklist,可以在 Checklist 中填入每一步待办事项,然后做完之后依次 check。即使卡片没有展开,在列表的缩略图上也能清晰地看到卡片有多少项任务没有做完。
我习惯于一定时间后整理 DONE 的卡片,使用快捷键 c
可以快速存档 (Archive) 卡片,用过 Gmail 的人应该非常清楚 Archive 的作用的。
和同类型的其他 Web 应用一样,Trello 网页版可以使用 ?
来查看支持的快捷键,除了上面提到的 c
还有很多可以提高效率的快捷键:
n
新建卡片#
在新建卡片时指定 Label,在输入卡片标题时直接输入 #1
就是添加第一个 Label,同理加数字即可^
后面加数字,表示添加到 List 中第几个j/k/left/right
来快速浏览,/.
用来左右移动卡片/
快速定位到搜索e
悬浮在卡片上,按下 e
可以快速编辑f
过滤,快速打开可以按照 Label 过滤x
清除所有过滤器l
给 card 添加 labelq
可以快速过滤已经 assign 给自己的卡片Space
快速将卡片 assign 给自己,或者快速移除s
开关 watch,关注一个 卡片之后,所有和卡片相关的操作都会收到提醒除了使用 Chrome 的扩展,还可以将下面这个页面保存为书签,点击书签就可以将当前页面保存到 Trello
https://trello.com/add-card
更多 Tip 可以参考这里
Trello Gold 类似于其他网站的高级会员,可以通过购买获取,也可以通过邀请获取。使用 Gold 会员有如下几个好处:
对于普通使用来说,这些额外的附加效果几乎可以忽略,都是一些低频次操作。但如果你是重度用户不妨先邀请身边的好友来先体验下 Gold 版的 Trello。比如你可以邀请小伙伴一起制定计划:
另外我有几个公开的 board,如果感兴趣可以加入:
如果看到这里你想要注册,那么通过我的邀请链接 注册,你我都将得到 1 个月的 Gold 会员。
Ctrl+a Ctrl+e Ctrl+u Ctrl+r
Ctrl+n Ctrl+p 等等
更多的快捷键和 bash 的内容可以参考这篇
绝大多数情况下会在终端来管理 git 项目,对 git 项目最好能够有一个直观的显示,包括当前的分支,修改的内容,命令行空间比较小,但是也能够显示分支名和是否有修改,推荐使用
oh-my-zsh
把每天要使用 5 次以上的命令都制作别名保存到 bashrc 或者 zshrc 中
alias vi='vim'
如果经常切换不同的终端窗口执行不同的任务,那么就需要考虑使用顺手的多窗口或者支持分屏 (panel) 的终端,推荐使用 Tmux,配合快捷键无比顺畅。
Tmux 的内容可以参考这个
除却常用的查找文件、浏览文件命令等等之外,善用命令行中的 Tab 自动补全,通配符等等。
cat, grep
通过文件名查找
sudo find -name <filename> path_to_search
滚动查看大文件
less path_to_file
当前路径下打开文件管理器
nemo .
树形结构展开当前目录结构,包括子目录和文件
tree
流式读取一个文件,实时日志文件
tail -f filename
Unix 哲学中,每个程序都足够小,只做一件事情,并将其做到最好。Bash 提供的管道机制 (|) 可以将命令的输出作为另一个命令的输入,结合两个或者多个命令,比如最简单的例子,ls 是将目录下文件列出, grep 命令是搜索包含指定正则的行,结合两者
ls ~ | grep word
就可以过滤 HOME 目录下,包含 word 的文件
*
星号字符匹配任意长度,比如删除文件夹下,指定文件
rm morning*.jpg
这样就删除了当前目录下所有 morning 开头的 jpg 文件,使用 rm 命令时一定要注意确认
>
字符可以将一个命令的输出重定向到一个文件或者另一个命令的输入,一般情况下命令会有一些输出结果
tree . > file.txt
可以将当前文件目录结构输出到文件 file.txt 中。
>
会覆盖输出的文件 >>
用来追加到文件末尾。
Bash 默认情况下会立即执行当前键入的每一条命令,通常我们就是这样要求终端的,但是如果想要某一些应用在后台长时间执行,可以使用 &
操作符,当然更加推荐 screen
或者 tmux
这类的工具。
./long_time_task.sh &
可以在后台执行一个长时间任务。
使用 htop 来查看系统资源,以及对进程进行管理,当然如果熟悉 ps 也可以使用 ps 来查看
大型 Java 项目可以考虑使用 JetBrains 系列产品,对于 Python, Bash 等脚本语言可以考虑使用 vim
之前一篇文章 已经分享过 [[tmux]] 的基本使用。这一篇就来总结一下 Tmux 下常用的插件。
Vim 有自己的插件管理系统,zsh 也有插件管理,那当然 Tmux 肯定有插件管理,其实学习 Tmux 的过程中,和 Vim 当时一样,所有的拷贝,粘贴的内容都是在 Tmux 和 Vim 的内部,和外部操作系统的粘贴板完全隔离了,我就是为了解决这个问题,才接触到了 Tmux Plugin Manager。
Tmux Plugin Manager 是一个 Tmux 插件管理器,用这个插件可以很方便的管理相关的插件。下文中可能把 Tmux Plugin Manager 简写成 tpm。
tpm 的安装的方法,在 GitHub 的页面非常清楚,git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
项目,在 .tmux.conf
文件中加入配置,重新加载配置即可。
# List of plugins
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
# Other examples:
# set -g @plugin 'github_username/plugin_name'
# set -g @plugin 'git@github.com/user/plugin'
# set -g @plugin 'git@bitbucket.com/user/plugin'
# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
run '~/.tmux/plugins/tpm/tpm'
新添加插件只需要,在配置文件中增加一行
set -g @plugin '...'
三个操作:
<prefix>
+ I 大写的 I (Install) 来安装新插件<prefix>
+ alt + u (uninstall) 来卸载。<prefix>
+ U,记住是大写的 U
.<prefix> + Alt + u
在 .tmux.conf
中加入
set -g @plugin 'tmux-plugins/tmux-yank'
然后使用 <prefix>
+ I 来安装 tmux-yank
Linux 平台下需要安装依赖 xsel 或者 xclip
sudo apt-get install xsel # or xclip
一些常用的快捷键
在普通模式下
<prefix>
+ y 来将命令行内容拷贝到 clipboard 系统粘贴板。<prefix>
+ Y 将当前 panel 的 working directory 拷贝到粘贴板在拷贝模式下复制到系统粘贴板
从 Tmux copy mode 直接打开选中内容,这个插件可以在 Tmux 的 copy 模式下,直接打开高亮选中的部分。
比较常见的比如选中一段网址,然后按下 o
,即可打开。
或者选中一个关键字,然后使用 Shift+s 来在搜索引擎中搜索。如果想要更换其他搜索引擎可以参考官方配置
Tmux 的 session 在机器关机再启动后就丢失了,使用 tmux-resurrect
可以将 session 保存到磁盘,再次启动机器的时候可以立即恢复。
安装:
set -g @plugin 'tmux-plugins/tmux-resurrect'
然后按下 prefix+I
安装。tmux-resurrect
只有两个简单的 key-binding,分别是保存和恢复:
prefix + Ctrl-s save
prefix + Ctrl-r restore
tmux-continuum 配合 tmux-resurrect 可以实现连续的保存 Tmux 的状态,如果自己的笔记本关机,那么 Tmux 的会话也会被销毁,下次开机需要重新创建,当然配合 fzf 之后倒也是没有那么麻烦,但如果开了 Pane,新建了 Windows,要恢复还是需要花费一段时间的,continuum 能够实时保存,重启后也能快速恢复。
安装:
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
然后启用:
set -g @continuum-restore 'on'
Tmux Sidebar 可以在 Tmux 中展示当前路径的目录树。非常适合和 Vim 一起使用。
Tmux Battery 在 Tmux 中显示电量和状态。
Log4j 是一个可靠的、高效的、快速可扩展的日志框架,Log4j 使用 Java 开发,已经被移植到了很多主流语言,比如 C, C++, Perl, Python, Ruby 等等。
这一切都可以通过一个配置文件来灵活地进行配置,而不需要修改应用代码。Log4j 是 Apache 的一个开放源代码项目。
在应用程序中添加日志记录总的来说基于三个目的:
Log4j 主要有三个组件:
log.info()
log.debug()
的地方,作用就是用来记录日志综合使用这三个组件可以轻松的记录信息的类型和级别,并可以在运行时控制日志输出的样式和位置。
Log4j 的执行顺序
Log4j 的特性:
通常,我们都提供一个名为 log4j.properties
的文件,该文件以 key-value 的方式进行配置。默认情况下,LogManager 会在 CLASSPATH 目录下寻找 log4j.properties
这个文件名。一些老的项目也会用 log4j.xml 格式来配置。
简单例子
# Define the root logger with appender X
log4j.rootLogger = DEBUG, X
# Set the appender named X to be a File appender
log4j.appender.X=org.apache.log4j.FileAppender
log4j.appender.X.File=${log}/log.out
# Define the layout for X appender
log4j.appender.X.layout=org.apache.log4j.PatternLayout
log4j.appender.X.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%t] %C{1}.%M(%F:%L) - %m%n
这个例子定义了
在 maven 的 pom.xml dependency 下添加:
<!-- sl4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
slf4j(Simple Logging Facade for Java) 不是一个真正的日志实现,而是抽象层,允许在后台使用任意一个日志类库。slf4j 使得代码能够独立于任意一个特定的日志 API。
SLF4J API 的特性占位符 (place holder),在代码中表示为“{}”的特性。占位符是一个非常类似于在 String 的 format() 方法中的 %s,因为它会在运行时被某个提供的实际字符串所替换。这不仅降低了你代码中字符串连接次数,而且还节省了新建的 String 对象。
然后在 CLASSPATH 下添加 log4j.properties
文件。
#config root logger
log4j.rootLogger = INFO,system.out
log4j.appender.system.out=org.apache.log4j.ConsoleAppender
log4j.appender.system.out.layout=org.apache.log4j.PatternLayout
log4j.appender.system.out.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%t] %C{1}.%M(%F:%L) - %m%n
#config this Project.file logger
log4j.logger.APPENDER_NAME.file=INFO,APPENDER_NAME.file.out
log4j.appender.APPENDER_NAME.file.out=org.apache.log4j.DailyRollingFileAppender
log4j.appender.APPENDER_NAME.file.out.File=logContentFile.log
log4j.appender.APPENDER_NAME.file.out.layout=org.apache.log4j.PatternLayout
在 Java 代码中使用 slf4j
private static final Logger logger = LoggerFactory.getLogger(Server.class);
logger.info("now {}" , "starting server");
Log4j 支持两种配置文件格式,一种是 XML 格式的文件,一种是 Java properties(key=value)【Java 特性文件(键 = 值)】。先介绍使用 Java 特性文件做为配置文件的方法
配置详细介绍
Loggers 组件在此系统中被分为五个级别:DEBUG、INFO、WARN、ERROR 和 FATAL。这五个级别是有顺序的
DEBUG < INFO < WARN < ERROR < FATAL
分别用来指定这条日志信息的重要程度,这里 Log4j 有一个规则:假设 Loggers 级别为 P,如果在 Loggers 中发生了一个级别 Q 比 P 高,则记录,否则就不记录。
比如,你定义的级别是 info,那么 error 和 warn 的日志可以显示而比他低的 debug 信息就不显示了。
配置 root Logger
其语法为:
log4j.rootLogger = [ level ] , appenderName1, appenderName2, …
# level : 是日志记录的优先级,分为 OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL 或者您定义的级别。Log4j 建议只使用四个级别,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了 INFO 级别,则应用程序中所有 DEBUG 级别的日志信息将不被打印出来。
appenderName: 就是指定日志信息输出到哪个地方。可以同时指定多个输出目的地。
例如:log4j.rootLogger=info,A1,B2,C3
Log4j 日志系统允许把日志输出到不同的地方,如控制台(Console)、文件(Files)、根据日期或者文件大小产生新的文件、以流的形式发送到其它地方等等。
其语法为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
log4j.appender.appenderName.optionN = valueN
其中, Log4j 提供的 appender 有以下几种:
例:
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
定义一个名为 stdout 的输出目的地, 输出到控制台。
其语法为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
“fully.qualified.name.of.appender.class” 可以指定下面五个目的地中的一个:
输出目的地的选项,可以通过如下语法指定
log4j.appender.appenderName.option = valueN
Java web 项目里面的日志的位置配置支持变量
如果是要指定日志文件的位置为 D 盘下的 log.txt 文件。
log4j.appender.APPENDER_NAME.file.out.File=d:\\log.txt
如果指定日志文件的位置为当前的 tomcat 的工作目录下的某个文件
log4j.appender.APPENDER_NAME.file.out.File=${catalina.home}/logs/logs_tomcat.log
DatePattern='.'yyyy-ww
: 每周滚动一次文件,即每周产生一个新的文件。当然也可以指定按月、周、天、时和分。即对应的格式如下:
按照日志大小滚动日志文件
实际应用:
log4j.appender.A1=org.apache.log4j.ConsoleAppender // 这里指定了日志输出的第一个位置 A1 是控制台 ConsoleAppender
如果希望格式化自己的日志输出,Log4j 可以在 Appenders 的后面附加 Layouts 来完成这个功能。Layouts 提供了四种日志输出样式,如根据 HTML 样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式等等。
其语法表示为:
org.apache.log4j.HTMLLayout 以 HTML 表格形式布局 org.apache.log4j.PatternLayout 可以灵活地指定布局模式 org.apache.log4j.SimpleLayout 包含日志信息的级别和信息字符串 org.apache.log4j.TTCCLayout 包含日志产生的时间、线程、类别等等信息
配置时使用方式为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class log4j.appender.appenderName.layout.option1 = value1 log4j.appender.appenderName.layout.option = valueN
“fully.qualified.name.of.layout.class” 可以指定下面 4 个格式中的一个:
Log4J 采用类似 C 语言中的 printf 函数的打印格式格式化日志信息,打印参数如下:
%m 输出代码中指定的消息
%p 输出优先级,即 DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该 log 信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows 平台为“\r\n”,Unix 平台为“\n”
%d 输出日志时间点的日期或时间,默认格式为 ISO8601,也可以在其后指定格式,比如: %d{yyyy MMM dd HH:mm:ss,SSS} ,输出类似: 2002 年 10 月 18 日 22 : 10 : 28 , 921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。
格式化例子:
log4j.appender.APPENDER_NAME.file.out.layout.ConversionPattern=%d{yyyy MMM dd HH:mm:ss,SSS}%5p{ \%F\:\%L }-%m%n
注意:
参数中间可能会有一些数字,比如:%5p 它的意思就是在输出此参数之前加入多少个空格,还有就是里面的“\”的作用是转义字符
LocationInfo=true: 默认值是 false, 输出 java 文件和行号
实际应用:
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
这里需要说明的就是日志信息格式中几个符号所代表的含义:
%p: 输出日志信息优先级,即 DEBUG,INFO,WARN,ERROR,FATAL,
%d: 输出日志时间点的日期或时间,默认格式为 ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002 年 10 月 18 日 22:10:28,921
%r: 输出自应用启动到输出该 log 信息耗费的毫秒数
%c: 输出日志信息所属的类目,通常就是所在类的全名
%t: 输出产生该日志事件的线程名
%l: 输出日志事件的发生位置,相当于 %C.%M(%F:%L) 的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
%x: 输出和当前线程相关联的 NDC(嵌套诊断环境), 尤其用到像 java servlets 这样的多客户多线程的应用中。
%%: 输出一个"%"字符
%F: 输出日志消息产生时所在的文件名称
%L: 输出代码中的行号
%m: 输出代码中指定的消息,产生的日志具体信息
%n: 输出一个回车换行符,Windows 平台为"\r\n",Unix 平台为"\n"输出日志信息换行
可以在 % 与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如:
这里上面三个步骤是对前面 Log4j 组件说明的一个简化;下面给出一个具体配置例子,在程序中可以参照执行:
log4j.rootLogger=INFO,A1,B2 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
根据上面的日志格式,某一个程序的输出结果如下:
0 INFO 2003-06-13 13:23:46968 ClientWithLog4j Client socket: Socket[addr=localhost/127.0.0.1,port=8002,localport=2014]
DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server says: 'Java server with log4j, Fri Jun 13 13:23:46 CST 2003'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j GOOD
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Command 'HELLO' not understood.'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j HELP
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Vocabulary: HELP QUIT'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j QUIT
当输出信息于回滚文件时
log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender // 指定以文件的方式输出日志 log4j.appender.ROLLING_FILE.Threshold=ERROR log4j.appender.ROLLING_FILE.File=rolling.log // 文件位置,也可以用变量 ${java.home}、rolling.log log4j.appender.ROLLING_FILE.Append=true log4j.appender.ROLLING_FILE.MaxFileSize=10KB // 文件最大尺寸 log4j.appender.ROLLING_FILE.MaxBackupIndex=1 // 备份数 log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
建议使用 lombok 的 @Slf4j 注解。
LOG4J 的配置之简单使它遍及于越来越多的应用中:Log4J 配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。
log4j.rootLogger=DEBUG,CONSOLE,FILE,ROLLING_FILE,SOCKET,LF5_APPENDER,MAIL,DATABASE,A1,im
log4j.addivity.org.apache=true
# 应用于控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=DEBUG
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
#log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[thread] n%c[CATEGORY]%n%m[MESSAGE]%n%n
#应用于文件
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=file.log
log4j.appender.FILE.Append=false
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
# Use this layout for LogFactor 5 analysis
# 应用于文件回滚
log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold=ERROR
log4j.appender.ROLLING_FILE.File=rolling.log // 文件位置,也可以用变量 ${java.home}、rolling.log
log4j.appender.ROLLING_FILE.Append=true //true: 添加 false: 覆盖
log4j.appender.ROLLING_FILE.MaxFileSize=10KB // 文件最大尺寸
log4j.appender.ROLLING_FILE.MaxBackupIndex=1 // 备份数
log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
#应用于 socket
log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost=localhost
log4j.appender.SOCKET.Port=5001
log4j.appender.SOCKET.LocationInfo=true
# Set up for Log Facter 5
log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[thread]%n%c[CATEGORY]%n%m[MESSAGE]%n%n
# Log Factor 5 Appender
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000
# 发送日志给邮件
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold=FATAL
log4j.appender.MAIL.BufferSize=10
log4j.appender.MAIL.From=web@gmail.com
log4j.appender.MAIL.SMTPHost=www.gmail.com
log4j.appender.MAIL.Subject=Log4J Message
log4j.appender.MAIL.To=web@gmail.com
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
# 用于数据库
log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
log4j.appender.DATABASE.user=root
log4j.appender.DATABASE.password=
log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File=SampleMessages.log4j
log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout
#自定义 Appender
log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender
log4j.appender.im.host = mail.cybercorlin.net
log4j.appender.im.username = username
log4j.appender.im.password = password
log4j.appender.im.recipient = corlin@cybercorlin.net
log4j.appender.im.layout=org.apache.log4j.PatternLayout
log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- ========================== 自定义输出格式说明 ================================ -->
<!-- %p 输出优先级,即 DEBUG,INFO,WARN,ERROR,FATAL -->
<!-- #%r 输出自应用启动到输出该 log 信息耗费的毫秒数 -->
<!-- #%c 输出所属的类目,通常就是所在类的全名 -->
<!-- #%t 输出产生该日志事件的线程名 -->
<!-- #%n 输出一个回车换行符,Windows 平台为“\r\n”,Unix 平台为“\n” -->
<!-- #%d 输出日志时间点的日期或时间,默认格式为 ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002 年 10 月 18 日 22:10:28,921 -->
<!-- #%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10) -->
<!-- ========================================================================== -->
<!-- ========================== 输出方式说明 ================================ -->
<!-- Log4j 提供的 appender 有以下几种:-->
<!-- org.apache.log4j.ConsoleAppender(控制台), -->
<!-- org.apache.log4j.FileAppender(文件), -->
<!-- org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件), -->
<!-- org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件), -->
<!-- org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方) -->
<!-- ========================================================================== -->
<!-- 输出到日志文件 -->
<appender name="filelog_appender"
class="org.apache.log4j.RollingFileAppender">
<!-- 设置 File 参数:日志输出文件名 -->
<param name="File" value="log/testlog4jxml_all.log" />
<!-- 设置是否在重新启动服务时,在原有日志的基础添加新日志 -->
<param name="Append" value="true" />
<!-- 设置文件大小 -->
<param name="MaxFileSize" value="1MB" />
<!-- 设置文件备份 -->
<param name="MaxBackupIndex" value="10000" />
<!-- 设置输出文件项目和格式 -->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p (%c:%L)- %m%n" />
</layout>
</appender>
<!-- 输出到日志文件 每天一个日志 -->
<appender name="filelog_daily" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="log/daily.log" />
<param name="DatePattern" value="'daily.'yyyy-MM-dd'.log'" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss\} %-5p] [%t] (%c:%L) - %m%n" />
</layout>
</appender>
<!-- 输出到控制台中 -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy-MM-dd HH:mm:ss} %-5p: %m%n" />
<!-- "%-5p: [%t] [%c{3}.%M(%L)] | %m%n" -->
</layout>
</appender>
<appender name="EMAIL_QQ" class="org.apache.log4j.net.SMTPAppender">
<param name="Threshold" value="INFO"/>
<param name="BufferSize" value="128" />
<param name="SMTPHost" value="smtp.qq.com" />
<param name="SMTPUsername" value="" />
<param name="SMTPPassword" value="" />
<param name="From" value="" />
<param name="To" value="" />
<param name="Subject" value="测试邮件发送" />
<param name="LocationInfo" value="true" />
<param name="SMTPDebug" value="true" />
<layout class="org.cjj.log4j.extend.PatternLayout_zh">
<param name="ConversionPattern" value="[%d{ISO8601}] %-5p %c %m%n"/>
</layout>
</appender>
<!--- 异步测试,当日志达到缓存区大小时候执行所包的 appender -->
<appender name="ASYNC_test" class="org.apache.log4j.AsyncAppender">
<param name="BufferSize" value="10"/>
<appender-ref ref="EMAIL_QQ"/>
</appender>
<!-- 设置包限制输出的通道 -->
<category name="com.package.name" additivity="false"><!-- 日志输出级别,起码可以有 5 个级别,可以扩展自己的级别,邮件发送必须是 ERROR 级别不好用,所以最后自己扩展一个邮件发送级别 -->
<level value="ERROR" />
<appender-ref ref="filelog_daily" />
<appender-ref ref="daily_appender" />
<appender-ref ref="console" />
<appender-ref ref="ASYNC_test" />
</category>
</log4j:configuration>
Web 配置 log4j, 需求增加以下内容到 WEB-INF/web.xml
${smilecargo.root}
是 web 工程相对路径
配置时出现如下问题:
log4j:ERROR setFile(null,true) call failed.
java.io.FileNotFoundException: /home/work/log/web.log (No such file or directory)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:270)
at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
at java.io.FileOutputStream.<init>(FileOutputStream.java:133)
at org.apache.log4j.FileAppender.setFile(FileAppender.java:294)
at org.apache.log4j.FileAppender.activateOptions(FileAppender.java:165)
at org.apache.log4j.DailyRollingFileAppender.activateOptions(DailyRollingFileAppender.java:223)
at org.apache.log4j.config.PropertySetter.activate(PropertySetter.java:307)
at org.apache.log4j.xml.DOMConfigurator.parseAppender(DOMConfigurator.java:295)
at org.apache.log4j.xml.DOMConfigurator.findAppenderByName(DOMConfigurator.java:176)
at org.apache.log4j.xml.DOMConfigurator.findAppenderByReference(DOMConfigurator.java:191)
at org.apache.log4j.xml.DOMConfigurator.parseChildrenOfLoggerElement(DOMConfigurator.java:523)
at org.apache.log4j.xml.DOMConfigurator.parseCategory(DOMConfigurator.java:436)
at org.apache.log4j.xml.DOMConfigurator.parse(DOMConfigurator.java:1004)
at org.apache.log4j.xml.DOMConfigurator.doConfigure(DOMConfigurator.java:872)
at org.apache.log4j.xml.DOMConfigurator.doConfigure(DOMConfigurator.java:778)
at org.apache.log4j.helpers.OptionConverter.selectAndConfigure(OptionConverter.java:526)
at org.apache.log4j.LogManager.<clinit>(LogManager.java:127)
at org.apache.log4j.Logger.getLogger(Logger.java:104)
at org.apache.commons.logging.impl.Log4JLogger.getLogger(Log4JLogger.java:262)
at org.apache.commons.logging.impl.Log4JLogger.<init>(Log4JLogger.java:108)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.apache.commons.logging.impl.LogFactoryImpl.createLogFromClass(LogFactoryImpl.java:1025)
at org.apache.commons.logging.impl.LogFactoryImpl.discoverLogImplementation(LogFactoryImpl.java:844)
at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:541)
at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:292)
at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:269)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:655)
at org.springframework.util.PropertyPlaceholderHelper.<clinit>(PropertyPlaceholderHelper.java:40)
at org.springframework.web.util.ServletContextPropertyUtils.<clinit>(ServletContextPropertyUtils.java:38)
at org.springframework.web.util.Log4jWebConfigurer.initLogging(Log4jWebConfigurer.java:128)
at org.springframework.web.util.Log4jConfigListener.contextInitialized(Log4jConfigListener.java:49)
at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:890)
at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:532)
at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:853)
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:344)
at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1514)
at org.eclipse.jetty.maven.plugin.JettyWebAppContext.startWebapp(JettyWebAppContext.java:359)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1476)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:785)
at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:261)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:545)
at org.eclipse.jetty.maven.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:434)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:131)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:113)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:113)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:167)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:131)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:113)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:113)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:131)
at org.eclipse.jetty.server.Server.start(Server.java:449)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:105)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:113)
at org.eclipse.jetty.server.Server.doStart(Server.java:416)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.maven.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:467)
at org.eclipse.jetty.maven.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:333)
at org.eclipse.jetty.maven.plugin.JettyRunMojo.execute(JettyRunMojo.java:180)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
at org.codehaus.classworlds.Launcher.main(Launcher.java:47)
log4j:ERROR Either File or DatePattern options are not set for appender [file].
解决方案:
这种情况一般是 log 文件的路径不对,要不是文件路径不存在,要不就是无权限写入。
因为平时使用 SmartGit 这样一个 Git client,所以也没有太大注意 Git 中不同阶段撤回的方式,虽然平时接触过 git reset
的 --soft
和 --hard
来撤销已提交的 commit,但没有形成一个系统的知识体系。大家都知道 Git 是一个分布式版本控制,所以 Git 会有一个本地库,和一个远端库,而平时提交代码的时候,一般也都是先从本地工作区提交代码
git add .
git commit -s
git push
这几个步骤,虽然平淡无奇,但是展开说,就体现了 Git 的重要的环节,一段代码的提交顺序:
工作区 -> git add . -> 暂存区 -> git commit -> 本地仓库 -> git push -> 远程仓库
这里就要提到 Git 中的四个区:
被追踪的文件,在未进入和进入上述四个区之后分别有一个状态,所以一共有五个状态:
在了解这几个基本概念之后,如何检查本地的修改,以及如何查看不同状态之间的修改,这就要用到 git diff
命令。
直接使用 git diff
命令,能够查看已修改,未暂存的内容
使用 git diff --cache
来查看已暂存,未提交的内容
使用 git diff origin/master master
来查看已提交,未推送的差异。
工作区 暂存区 本地仓库 远程仓库
\ / \ / \ /
\ / \ / \ /
git diff git diff --cache git diff origin/master master
图解
在知道如何查看四个不同区之间的差异后,如何使用 git reset
来撤销呢?
如果只是在编辑器中修改了文件的内容,还未使用 git add
将修改提交到暂存区,那么可以使用 git checkout .
或者 git checkout -- <file>
来丢弃本地全部修改或者丢弃某文件的修改。
可以将 git add .
和 git checkout .
看做一对反义词,修改完成后,如果想 Git 往前进一步,让修改进入暂存区,执行 git add .
如果向后退则执行 git checkout .
如果已经执行了 git add
,意味着暂存区中已经有了修改,但是需要丢弃暂存区的修改,那么可以执行 git reset
对于已经被 Git 追踪的文件,可以使用
git reset <file>
来单独将文件从暂存区中丢弃,将修改放到工作区。
对于从来没有被 Git 追踪过,是 new file 的文件,则需要使用:
git reset HEAD <file>
来将新文件从暂存区中取出放到工作区。
如果确定暂存区中的修改完全不需要,则可以使用
git reset --hard
直接将修改抛弃,谨慎使用 –hard 命令, 暂存区中所有修改都会被丢弃。修改内容也不会被重新放到工作区。
对于已经本地的提交,也就是使用 git add
并且执行了 git commit
的修改,这时候本地的修改已经进入了本地仓库,而这是需要撤销这一次提交,或者本地的多次提交,怎么办?
git reset --hard origin/master
同样还是 git reset
命令,但是多了 origin/master
,origin
表示远端仓库的名字,默认为 origin,可能也有其他自己的名字,origin/master
表示远程仓库,既然本地的修改已经不再需要,那么从远端将代码拉回来就行。
不过不建议直接使用 git reset --hard origin/master
这样太强的命令,如果想要撤销本地最近的一次提交,可以使用
git reset --soft HEAD~1
这行命令表示,将最近一次提交 HEAD~1
从本地仓库回退到暂存区,--soft
不会丢弃修改,而是将修改放到暂存区,后续继续修改,或者丢弃暂存区的修改就可以随意了。如果要撤销本地两次修改,则改成 HEAD~2
即可,其他同类。
不过要注意的是,已经提交到远端的提交,不要使用 git reset
来修改,对于多人协作项目会给其他人带来很多不必要的麻烦。
对于已经推送的修改,原则上是不要撤销的,不过 Git 给了使用者充分的自由,在明确自己在做什么的情况下,可以使用 git push -f
使用 force 选项来将本地库 force 覆盖远端仓库,强制 push 到远端。
对于个人,一个人使用的项目使用这样的方式,并没有太大问题,但是如果对于多人项目,如果你强行改变了远端仓库,别人再使用的时候就会出现很多问题,所以使用 git push -f
时一定要想清楚自己在做什么事情。
MyBatis 是 Java 系的 ORM(Object Relational Mapping) 框架,提供了非常简洁的编程接口。用简单的话来说就是可以将数据库表映射到 Object 中 MyBatis 就是中间辅助处理的框架。
类似于 Python 中的 [[SQLAlchemy]]。
分为三层
基础支持层包含了如下模块
核心处理层包括
mybatis-config.xml
配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 Configuration 对象中接口层相对较简单,核心是 SqlSession
接口,接口定义了 MyBatis 暴露给应用程序的 API。
所以如果要使用 MyBatis 基本有如下几个步骤:
SqlSession 是 MyBatis 关键对象,持久化操作的对象,类似 JDBC 中 Connection。SqlSession 对象完全包含以数据库为背景的所有执行 SQL 操作的方法,底层封装了 JDBC 连接。每个线程都应该有自己的 SqlSession 实例,SqlSession 实例线程不安全,不能共享,绝对不要将 SqlSession 实例引用放到类静态字段或者实例字段中。使用完 SqlSession 一定关闭。
Mapper 文件针对 SQL 文件构建。
select 语句用来映射查询语句。
<select id="selectUser" parameterType="int" resultType="hashmap">
SELECT * FROM USER WHERE ID = #{id}
</select>
这个语句被称为 selectUser,接受 int 参数,返回 HashMap 类型。
比如
<insert id="insertUser">
insert into USER (id, username, password, email, address)
values (#{id},#{username},#{password},#{email},#{address})
</insert>
sql 元素用来定义可重用的 SQL 代码。
如果 parameterType 传入一个对象,那么 #{id}
在查询时会去对象属性查询。
<insert id="insertUser" parameterType="User">
insert into USER (id, username, password, email, address)
values (#{id},#{username},#{password},#{email},#{address})
</insert>
ResultMaps 元素是 MyBatis 中最重要最强大的元素,告诉 MyBatis 从结果集中取出数据转换成 Java Object。
MyBatis 是一个比较大的项目,下面包含了很多子项目,如果看这个项目列表就能够清晰的看到一些
Ant 则直接在 classpath 引入 jar 包,Maven 则
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
使用 mybatis-spring 将 MyBatis 无缝嵌入到 Spring 中。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>x.x.x</version>
</dependency>
mybatis generator 作为插件引入:
<project ...>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
</plugin>
...
</plugins>
...
</build>
...
</project>
更多关于 MyBatis Generator 的内容可以参考这里
pandas 基于 numpy 构建,可以提供强大的数据处理分析能力。
两种数据类型,series 和 dataframe
数据集
series 是一种一维数据类型,每个元素都有各自的标签。可以当成带标签元素的 numpy 数组,标签可以是数字或者字符。Series 可以用元组、列表或者字典生成,如果没有为数据指定标签,那么会自动生成 0 到 N-1 的标签。
obj = Series([4, 7, -5, 3])
obj2 = Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
值和标签分别可以通过 .values
和 .index
来访问
dataframe 是一个二维、表格型的数据结构,每个轴都有标签。
data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
'year': [2000, 2001, 2002, 2001, 2002],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}
frame = DataFrame(data)
读 csv 文件
pandas.read_csv('path.csv')
写 csv 文件
data.to_csv('filepath.csv')
读 csv 这个函数有非常多的参数,比如读很大的文件,只想要读前 500 条,那么可以使用
pandas.read_csv('path.csv', nrows=500)
读 \t
分割的表格可以使用
pandas.read_table()
同理这个方法也有非常多的参数,可以具体参考文档。
还有读取等宽数据
pandas.read_fwf()
读取 Excel
pandas.read_excel()
读取 JSON
pandas.read_json()
读取网页
pandas.read_html()
读取 SQL 有
pandas.read_sql_table()
pandas.read_sql_query()
pandas.read_sql()
还有其他包括 Google BigQuery,HDFS,SAS 等等数据来源的,可以参考官方文档的 API reference。
nltk 是 Python 下一个自然语言处理相关的库,可以方便的实现分词,词性标注等等。
pip install nltk
然后在终端执行 python, 进入交互式编辑环境
>> import nltk
>> nltk.download()
下载相关模块
nltk.sent_tokenize(text)
#对文本按照句子进行分割
nltk.word_tokenize(sent)
#对句子进行分词
nltk.pos_tag()
from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
lemmatizer.lemmatize(word)
frp 是 fatedier 的开源项目,frp 是一个高性能的反向代理应用,可以轻松地进行内网穿透,对外网提供服务,支持 TCP, UDP, http, https 等协议类型,并且 web 服务支持根据域名进行路由转发。
frp 用法和 ngrok 相似,但是 frp 比 ngrok 更加优秀。 配置过程很简单,但是也遇到一些问题,所以把过程记录下来。
frps.ini
服务端配置:
[common]
bind_port = 7000
frpc.ini
客户端配置
[common]
server_addr = x.x.x.x # 填写公网服务器 IP 地址
server_port = 7000
[ssh-computer-name]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000 #配置服务器端口
然后使用 ssh user@x.x.x.x -p 6000
或者 ssh -o Port=6000 user@x.x.x.x
来连接内网的机器。
再配置完之后可以使用 supervisor 来管理,实现进程死掉后自动重启。