之前在折腾 [[AdGuard Home]] 的时候偶然间了解到了 NextDNS,简单了解一下 NextDNS 能做的事情相当于把 AdGuard Home 在本地的 DNS 处理给部分搬到了云端。虽然提升了一定的可用度,毕竟不是所有的设备都在 AdGuard Home 的网络中。然后看到 NextDNS 还支持 Web3,看设置界面就已经支持了 ENS,HNS 等等。
但之所以没有深入使用就是因为 DNS 还是一个非常基础的服务,但是 NextDNS 在国内并没有节点,所以导致连接经常出问题,要不就是连接不上,要不就是拖慢了解析速度。
NextDNS 是一个私人的 DNS 解析服务器。它提供了一种更安全、更私密的互联网浏览方式。通过将所有网络流量路由到 NextDNS 服务器上,NextDNS 可以过滤广告和恶意内容,阻止跟踪器,并提供家长控制功能。同时,NextDNS 还支持自定义规则和白名单,用户可以根据自己的需求对互联网体验进行定制。此外,NextDNS 还提供了详细的统计数据和日志记录,方便用户了解自己的网络使用情况。总之,NextDNS 是一个强大而灵活的工具,可以提供更安全、更私密的互联网浏览体验。
原理是 NextDNS 通过 DNS 层面来阻挡任何安全威胁,包括屏蔽广告,阻挡 trackers,保护网络中的用户安全。
NextDNS 可以通过 DNS 查询过滤,遇到匹配规则的域名,返回不存在的地址来实现广告和追踪的特性。
免费用户提供每个月 300000 次的查询。
NextDNS 有时候会因为服务器分布位置的差异,比如将 Google 解析到其他地区的服务器 IP 上,导致访问 Google 的时候变慢。
在上次总结了2020读书笔记 之后,Tai 问我如何通过 Zettelkasten 和 Obsidian 来实践我的笔记。这里就也正好总结一下我自己的方法,希望可以在分享过程,或者和大家的讨论中来获取更多有效率方法。
我是如何来记笔记的?或者更具体一些,如何来记读书笔记的。就像是 How to read a book 中所说那样,不同类型的书有不同的阅读方式。其实笔记也一样,针对的内容不同,记笔记的方式也不尽相同。
每当我拿起一本新的书,我通常会去豆瓣图书看一下这本书的相关介绍,然后创建一个新的笔记,将作者等等信息,在笔记头部 meta data 部分记录一下。
source:
category:
tag:
time:
Zettelkasten 卡片的第一个准则,就是一条笔记只记录一个想法。
所以在完成这本书的 meta data 之后,我会给这个笔记打上一些分类,比如读书笔记,Reading-2020,Reading-2020Q4 这样的分类,这样以后我直接去这些单独的分类就能一下看到关联的书,也能快速知道比如在 Reading-2020Q4 这个季度看过的书了。
一旦读书的过程中产生新的想法,有可能是因为书中所涉及的观点,将很久以前想表达的东西说了出来,或者是书中描述的内容和我自己的切身经历,或者和已有的想法有关联,我会立即使用 ⌘+⇧+N 创建一个新的 Zettelkasten 模板文档,这个文档的标题是当下的想法,然后 source,连接到之前创建的书的笔记。
这个时候我不会花费很多的时间来记笔记,而是专注在书上。一旦完成笔记就立即转到书的文本中。
当完成一本书之后,在笔记中进入该书的笔记,会有一连串连接到该笔记的想法,这个时候就是结合这些想法和整本书的结构,快速的整理出这本书作者在讲述什么。用自己的语言,将其整理一下。这个时候才会知道上学的时候总结中心思想的真正含义是什么。如果我能够用我自己的语言组织出来,才能算是真正意义上读过这一本书。
当整理出整本书在讲述的内容之后,然后在去看关联的想法笔记,对这些笔记做更进一步的解释,如果有相关联的笔记,通过连接关联起来。
书籍还有一个比较重要的便是阅读过程中的标注或者是高亮部分,我个人因为不是固定使用一个设备来阅读,所以有可能:
针对不同的格式和媒介,相应个管理方式也不太一样,Moon+ Reader 和微信读书都可以在 Android 应用上导出纯文本的高亮,我直接分享到 Markor 生成一个新的笔记,放到同步目录即可。
而 Kindle 上的高亮部分通常是间隔一段时间导出,然后通过 kindle-notes 项目整理成一个网页。
而大部分的 PDF ,都是通过 Boox 来阅读了,影印版的 PDF 的高亮部分不是很容易导出,所以只能先存放在设备中了。
对于专业领域的笔记,我更加关心的是这一个技术栈,所以一般我会以一个 Zettelkasten 的中间层笔记开始,新建一个 「XXX 知识点」的笔记,这样以后每一次我有新的技术相关的笔记,我可以在分类上直接连接到这个「知识点」的笔记上。等之后周期性的梳理 XXX 知识点的时候,我就可以看到连接到这个知识点的其他分散的笔记在什么地方。
另外一种方式便是当看一本相关的书籍的时候会有一系列的概念和知识,这个时候我会在「XXX知识点」这个文档中通过 Obsidian 提供的 [[]]
语法提前梳理好我有疑惑,并且需要单独研究的条目,当独到这些专题的时候,扩充这些子主题。
在人文社科类的读书笔记中,我更关心的是如何去解释我看到的名词,或者说概念,比如说什么是 [[民族主义]],什么是 [[极权主义]],另一个关心的点是书的作者,这可能是一个个人的习惯,我看书常常会是盯着一个作者的书看,从小学时候在图书馆把 Jules Verne 的书一本一本翻过来,到之后看金庸,东野圭吾,在到看福山,哈耶克,卢梭,只要这位作者有一本书让我觉得不错,我就会去看他其他的作品。
人文社科的图书往往是会形成新的认知,不像是专业类的书籍,会有很多固定,需要记忆的,一本好的社科图书会带领读者进行思考,很多书往往是通篇在解释一个概念。
记得大学唯一一门哲学课,老师强调了一学期的「下定义」,当我们谈论一个东西的时候,要首先知道这个东西是什么。
以[[极权主义]] 为力,我从不同的书中,看到不同的作者对其的描述。
而随着阅读相关的书籍,会产生越来越多的关于什么是极权主义的解释,也可以更进一步的让我去解释。
而同样的,在这个笔记下,又会产生其他许多的主题,比如极权主义下的法律制度,教育,治安等等话题。这样随着阅读量的增多,通过笔记和笔记之间的相互关联会使得心中的那个概念越来越熟悉。
上面说了那么多,其实涉及到的方法大多来自于 Zettelkasten:
当学会以这样一个方式来记笔记之后,就不必在拘泥于工具,甚至可以在现有工具的基础之上,或者自己重新构建一套自己的工具。而在此之前最要的就是理解这个方法的精髓,以及这样的思维方式能带来什么样的变化和提升。
而目前我使用最顺手的就是 Obsidian。
Obsidian 的 core plugin 就支持 Zettelkasten prefixer,然后我绑定了快捷键,⌘+Shift+N
,就是创建一则 Zettel 笔记,而普通的 ⌘+N 就是普通的笔记。
我的模板现在比较简单:
source:
category:
tag:
time:
说明:
[[]]
来关联#
另外一个模板功能,就是当我使用 ⌘+⇧+I 的时候,会弹出我之前设定的模板文件夹中的模板让我选择,直接插入到当前的文件中。
设定一些常用的模板,比如图书的模板,我会加上 author 等等其他的 metadata。
Zettelkasten 另外一个核心的观点就是,所有的笔记都在一起,可以一起被搜索,一笔被修改。这样就使得我寻找一个跨平台同步的解决方案,具体可以见这篇文章。
电脑虽然在白天随时可以访问到,但是手机还是更经常被用到。所以我使用 Markor 来随时记录。任何想法第一时间内记录下来。
时常回顾自己的笔记,Zettelkasten 方法实际要求当记下每一个笔记的时候,都要考虑当前的笔记和之前历史的笔记的关联,但是大部分情况下我并没有完全按照 Zettelkasten 的方法执行,我会定期,以及以随机的方式来访问一个笔记,然后阅读该笔记,建立这个笔记的关联。
经过上面有意识的创建和整理笔记之后,打开 Obsidian 的 Graph 可以看到的是,笔记会因为连接形成几个聚合的中心,几个中心我有意识的就直接沿用了我博客的几个分类。然后通过这些中心的节点我又能知道我关注的重点是什么来进一步的完善我的笔记。
之前的文章也提到过,这两年来我越来越意识到我的问题并不是看得东西不够多,而是往往不能理解我所看过的东西。一方面来随着时间的推移会忘记,一方面当阅读的时候无法理解所看到的东西时,记忆也就没有那么深刻了。所以我之后的目标之一便是我要能理解我写下的东西。
这些天翻箱倒柜,翻出来一些大学时候的文档,其中一篇是当时上外教课时打印给我们的材料,虽然可以看得到当时也在上面做过笔记,但现在已经完全不记得有这样一份文档的存在了。但回过头再看文档的内容,每一句话都是非常珍贵的建议,是一份那个时期完全需要读一下的材料,但当时却并没有好好珍惜。这份文档的名字叫做「An open letter to those who want to start programming」,如果记得没错的话这份文档交到我手上的时候,应该就是我刚去学习如何编程的时候。如果当时就能够理解这一份文档内容的话,这些年来我肯定能少走不少的弯路。
在结合到最近陆陆续续看过关于认知的一些书,才发现我自身的问题并不是在于看得东西不够多,而是没有完全理解我曾经看过的东西。就如这一份文档那样,现在的时间来看文档上列举的内容都是当下非常好的建议,然而当时的我并不理解,所以自然而然就忘记了这一份文档上的建议。
先来总结一下这一份文档中作者给出来的几个建议:
文中的内容大部分内容都在我之后对编程这个领域进一步的认识之后,陆陆续续才发现这每一条建议的可贵之处。我不知道是不是每个专业都会有这样一封写给初学者的信,但无疑一个专业领域前辈所写的内容对后来者的启发意义都是巨大的。
An open letter to those who want to start programming
First off, welcome to the fraternity. There aren’t too many people who want to create stuff and solve problems. You are a hacker. You are one of those who wants to do something interesting.
“When you don’t create things, you become defined by your tastes rather than ability.”
– WhyTheLuckyStiff
Take the words below with a pinch of salt. All these come from me – a bag-and-tag programmer. I love to get things working, rather than sit at something and over-optimize it.
Start creating something just for fun. That’s a great start! There’s no way you will start if you say you “need to learn before doing”. Everybody’s got to start somewhere. Fire up your editor and start writing code.
Here’s something important which people might call bad advice, but I’m sure you’ll stand by me when I’m finished saying why. Initially, screw the algorithms and data structures. They do not have generic use-cases in most simple applications. You can learn them later when you need them. Over a period of time, you’ll know what to apply in situations. Knowing their names and what they do would suffice to be able to pick some paper, dust it and implement it. And that is… if no library (other programmers’ re-usable code) is available, to do it in the programming language of your choice.
Choose a good language. One that you think you can produce something useful in short time.
So let C not be your first language. That might give you the satisfaction of doing things the really old-n-geeky way. C was the solution to the problem Assembly Language was. It offers better syntactic sugar than it’s prominent predecessor – Assemble Language. But today, C (or C++) is not a language that you can produce something very quickly. I would suggest that you use a dynamic language – I won’t sideline any options. Choose a language whose syntax (and documentation) you think you might be comfortable with. For this, you might want to spend some time trying out different languages for a few hours. The purpose of choosing such a language is not to make you feel better and that programming is easy. Completing stuff faster and being able to see the output keeps you motivated. Don’t choose a language that requires a special heavy-weight IDE (tool that helps you write code and run it) to program better in the language. All you should need is a text editor.
Choose a good editor.
An editor is to a programmer, like how a bow is to an archer. Here are some editors to get started with…
Watchout! Emacs and Vim might be really old. But they both have some features which even most modern editors don’t have.
Use an operating system that’ll teach you something.
Windows won’t teach you anything. The only thing you learn using Windows is to click the .exe file to install the software and use it. It may seem cool in the beginning, but in the long run when you have to deploy applications, especially if you are aspiring to be a web developer, you’ll need atleast basic knowledge of linux. Linux also allows you to customize stuff the way you need them to be. Macs are cool too, but I assume that you cannot afford one of those now.
Don’t copy-paste files to backup stuff.
It’s usual among amateur programmers to copy-paste files to some temporary directory in order to backup them. That’s the only way they seem to know. Stop that! Use a version control software. I strongly suggest Git, since it’s popular and easy to use. It has nice community and resources to support new-comers. (Apart from Git, There’s mercurial, darcs, fossil, etc. But just start with Git. I’m not going to bother you with the reasons for suggesting Git).
Know where to get help.
Join a community that you can relate to (with the tools you use). StackOverflow is Facebook for programmers. There are no status messages and comments. Instead there are questions and answers. Also learn to use the IRC. It’s an old form of chatrooms and is now being used by mostly developers to share information and helping each other.
Develop your netiquette.
Know when to ask questions. Most problems you face might have been stumbled upon by others who might have already posted on the internet for answers. Before asking on IRC or any forums, google first (or should I say blekko first) to see if there’s already a solution to your problem. IRC needs patience. Remember people are helping you for free out of goodwill. Sometimes it might take hours, for someone in the chatroom to respond to you. So wait until they do. Besides, be polite. It’s a small world. Karma, good or bad, comes back.
Meet people, because books only teach you routine stuff (oh and the “book” is dead they say).
There are some street smarts that you’ll learn when you tinker with stuff or learn from those who do it. Roam, meet people and say hello. You are not the only programmer in your place. Make friends and do stuff with them. If you’ve noticed, when a couple geeks get together, whatever the starting point of the conversation be, it always ends up getting technical. It’s bound to happen. Enjoy it. Programming for a good number of years, I can tell you that I learnt nothing more than what the books and articles said, until I starting meeting people and getting technical with them 6yrs back. So I always say that I’ve been programming for 6yrs, because that’s when I started meeting people and feel I really started to learn.
Write opensource code.
Writing opensource code is giving back. It’s much more than charity. You are leaving code that others can use and improve on (maybe) for years to come. It also helps you refine your skills when someone else adds to your code or suggests changes. Code that you opensource doesn’t have to be big. It can even be a useful little program that downloads youtube videos. Moreover, you’ll be surprised, that your code will often help you start and have interesting conversations with people.
Lastly, when years pass, return this favour, by writing a similar letter to someone else who asks you for such help. And possibily correct me.
--
For a hacker, by a hacker
Akash Manohar
Source: http://blog.akash.im/an-open-letter-to-those-who-want-to-start
2020 年注定是会在历史上留下不平凡的一年,年初的疫情到年底问题依然还在,而且在不确定疫苗的情况,在加上英国的病毒变异情况,不清楚到什么时候才是结束。再回头看今年的大事小事,从年初的李文亮事件,到年末的蛋壳,以及阿里被禁止上A股,有些事情发生地太突然,来不及思考,但只有思考,不仅是在事前的还是事后的思考都有其价值。
李文亮因言获罪,而密尔早在两百年前就曾经说过,即便是荒谬的言论也不得限制,对该言论的讨论过程能进一步证明相反观点的价值。而李文亮事件就是典型的「被压制的言论包含部分的真理」的情况,而正是因为言论的被压制,终究封城是阻止不了病毒的扩散的。而在看美国宪法,以及关于[[宪法第一修正案]]关于言论自由边界的书,能看到为了维护不进行事先审查言论的权利,经过了多少的争论。
而换一个角度来看,来看看一位金融从业者在[[非对称风险 笔记]]中的观点,「持续暴露在小概率风险下,即使爆仓风险的概率小到万分之一,那么在持续、重复的过程中爆仓的概率会越来越大」。相同的观点套用到现实,能禁止一个人说话,能阻止一群人发表观点,但只要危机没有被解决,终究会造成无法挽回的损失。
金斯伯格大法官离世后,先是让我认识了这位饱受尊敬的高龄大法官,然后将我带回了最高法院,在最高法院发展的几百年历史中,让我认识到了自由派的[[浪漫主义]]者 [[霍姆斯大法官]],也让我了解了未曾当过大法官却深刻影响了最高院的 [[勒尼德·汉德]] 法官,以及到更近代的第一人女性大法官[[奥康纳大法官]],当代的自由派[[苏特大法官]],以及许许多多以前可能听过但不曾有印象的名字,这些躲在背后的大法官们在一件件的案件,以及对这些案件的判决中,形象更加丰满起来。
如果说上半年收获最大的作者是 [[塔勒布]] 的话,那么下半年收获最大的作者便是 [[弗朗西斯·福山]],早在去年就看过其[[The Origins of Political Order]],但是碍于当时的认知和环境,实际上并没有完全读懂,今年先读了其之前的著作 [[历史的终结与最后的人]] ,然后再看其更进一步的论述 [[The Origins of Political Order]],才理清了其脉络。
之前两篇文章简单的介绍了 [[Logback]] 是什么,以及基本的使用,这一篇文章着重说一下 Logback 中最重要的 logback.xml
配置文件的编写。
![[Pasted image 20201210145047.png]]
配置文件格式:
<configuration>
<appender> //输出到控制台的信息配置
//....
</appender>
<appender> //输出到info文件的配置
//...
</appender>
<appender> //输出到error文件的配置
</appender>
<logger> //特殊处理日志定义
//..
</logger>
<root level="debug"> //总日志开关
//...
</root>
</configuration>
根节点 <configuration>
包含三个属性:
scan
: 为 true 时,如果配置改表会重新加载,默认是 truescanPeriod
: 检测配置文件修改的时间间隔,默认单位毫秒,默认时间间隔 1 分钟debug
: 为 true 时,打印 logback 内置日志信息,实时查看 logback 运行状态,默认 false例如:
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!--其他配置省略-->
</configuration>
子节点 <contextName>
,设置上下文名称。
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>myAppName</contextName>
<!--其他配置省略-->
</configuration>
用来定义变量值,两个属性 name
和 value
,通过 <property>
定义的值会被插入到 Logger 上下文中,可以使用 ${}
来使用。
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="APP_Name" value="myAppName" />
<contextName>${APP_Name}</contextName>
<!--其他配置省略-->
</configuration>
获取时间戳字符串
java.text.SimpleDateFormat
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<contextName>${bySecond}</contextName>
<!-- 其他配置省略-->
</configuration>
appender 是负责写日志的组件,两个属性:
name
,指定 appender 名字class
,指定 appender 全限定名logback 实现了一些内置的 appender,可以将日志输出到控制台,文件等等地方。
<appender name="file-appender" class="ch.qos.logback.core.rolling.RollingFileAppender">
....
</appender>
内置的 appender:
将日志输出到控制台(Console),两个子节点:
<encoder>
: 对日志输出格式化<target>
: 字符串 System.out
或 System.err
例如:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
将日志输出到文件(FileAppender),子节点:
<file>
: 被写入的文件名<append>
: 如果是 true,日志被追加到文件结尾,false 则清空现存文件,默认为 true<encoder>
: 对记录事件进行格式化<prudent>
: 如果是 true,日志会被安全地写入文件(其他 FileAppender 也在向此文件做写入操作)效率低,默认是 false<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.log</file>
<append>true</append>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
滚动日志,记录日志到文件,当符合某条件时,将日志记录到其他文件。子节点:
<file>
: 文件名<append>
: true 时,追加到文件结尾,false 时 清空现存文件,默认为 true<rollingPolicy>
: 滚动策略,定义 Rolling 的行为策略定义 class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"
根据时间来记录日志。
<fileNamePattern>
: 文件名的命名方案,%d
可以包含一个符合 java.text.SimpleDateFormat
格式的时间,比如 %d{yyyy-MM-dd}
。如果直接使用 %d
默认是 yyyy-MM-dd
<file>
: 当前日志总是记录到 file 指定的文件,非必需<maxHistory>
: 可选,保留归档文件的最大数量比如配置每天生成一个日志文件,保存 30 天日志。
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
定义 class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"
,超过大小滚动。
<maxFileSize>
: 默认大小是 10MB<prudent>
: 当为 true 时,不支持 FixedWindowRollingPolicy<minIndex>
: 窗口索引最小值<maxIndex>
: 窗口索引最大值<fileNamePattern>
: 必须包含 %i
,假设最小和最大为 1 和 2,命名模式是 mylog-%i.log
,会产生归档文件 mylog-1.log
和 mylog-2.log
。可以指定压缩选项。按照固定窗口模式生成日志文件,文件大于 5 MB 时,生成新的日志文件,窗口大小是 1 到 3,保存了 3 个归档文件后,覆盖最早的日志。
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>tests.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
异步日志
<appender name ="ASYNC_UTIL_LOG" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>512</queueSize>
<neverBlock>true</neverBlock>
<appender-ref ref="UTIL_LOG"/>
</appender>
<logger name="com.test.util" level="DEBUG" additivity="false" >
<appender-ref ref="ASYNC_UTIL_LOG"/>
</logger>
还有其他的 Appender 具体可以参考官方文档:
用来设置某一个包或具体的某一个类的日志打印级别,执行 appender。
Logger 是 Logback 另一个重要的组成部分,开发者可以将日志信息用特定的等级输出。Logback 定义了 5 个等级的日志级别:TRACE, DEBUG, INFO, WARN, ERROR。
每一个等级都对应着一个特定的方法, trace()
, debug()
, info()
, warn()
, error()
。
<!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" />
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" />
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />
<!--myibatis log configure-->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
根据不同的环境,配置不同的日志输出。
日志的名称不是 logback.xml
,如果要使用 Spring 扩展,要以 logback-spring.xml
命名。
<springProfile name="test,dev">
<logger name="com.dudu.controller" level="info" />
</springProfile>
<!-- 生产环境. -->
<springProfile name="prod">
<logger name="com.dudu.controller" level="ERROR" />
</springProfile>
Logback 在之前的文章中说过,是一个开源日志组件。
[[Logback]] 推荐和 SLF4J 一起使用。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<logback.version>1.1.7</logback.version>
<slf4j.version>1.7.21</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
</dependencies>
logback 简单配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder
by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
具体的注释写在配置代码中。
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 Logback 的配置中使用相对路径-->
<property name="LOG_HOME" value="/home" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 配置每天滚动生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
在 Java 代码中
public class App {
private final static Logger logger = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
logger.info("logback 成功了");
logger.error("logback 成功了");
logger.debug("logback 成功了");
}
}
[[Logback]] 是 log4j 的创始人开发设计的另一个开源日志组件,期望成为 log4j
项目的继任者,相较于 log4j,Logback 有一些优势。
Logback 划分成三个模块:
Maven 依赖:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
应用在启动时按如下的顺序寻找配置文件:
classpath
中 logback-test.xml
文件logback.groovy
logback.xml
META-INF/services/ch.qos.logback.classic.spi.Configurator
中的 logback 配置实现类BasicConfigurator
来配置,并将日志输出到 console。配置文件 logback-test.xml
或 logback.xml
都不存在, logback 会默认调用 BasicConfigurator,创建最小化配置。最小化配置由一个关联到根 logger 的 ConsoleAppender 组成。输出用模式为 %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
的 PatternLayoutEncoder 进行格式化。root logger 默认级别是 DEBUG。
配置文件格式:
<configuration>
<appender> //输出到控制台的信息配置
//....
</appender>
<appender> //输出到info文件的配置
//...
</appender>
<appender> //输出到error文件的配置
</appender>
<logger> //特殊处理日志定义
//..
</logger>
<root level="debug"> //总日志开关
//...
</root>
</configuration>
configuration
开头,后面有 0 个或多个 <appender>
元素,0 个或多个 <logger>
,最多有一个 <root>
元素。[[2020-12-10-logback-xml-config]] [[2020-12-10-logback-usage]]
很早以前就用过 [[GitBook]] 来将 Markdown 生成网页1,但是后来 GitBook 命令行工具不再持续的更新,开发团队转向了维护商业版本的 GitBook 之后就用的少了。
但随后就发现了使用 [[Rust]] 编写的 [[mdBook]],体验和 GitBook 一致,基本上可以完美的代替 GitBook。有趣的是官方的介绍也是对标 GitBook 的:
Create book from markdown files. Like Gitbook but implemented in Rust.
官方网站:
因为 mdBook 依赖与 Rust 所以需要安装 Rust 环境。
然后执行如下命令即可:
cargo install mdbook
初始化:
mdbook init
构建:
mdbook build
监控更改:
mdbook watch
启动一个本地服务:
mdbook serve
清理:
mdbook clean
要生成页内目录可以使用 toc
cargo install mdbook-toc
Python 的文档工具 [[mkdocs]] :
tag: #GitBook #wiki #Book #Markdown #note #writing #个人知识管理
IdeaVim 是 IntelliJ IDEA 编辑器下一款模拟 Vim 模式及快捷键的开源插件。鉴于大部分的时间都在 IntelliJ IDEA 下工作,所以总结一下在 IDEA 下使用 Vim 的一些快捷方式。
阅读完本文之后,你可以
~/.ideavimrc
来复用 Vim 的工作方式,即使将工作环境切换到 Terminal 下也可以沿用,并且充分利用 Idea 提供的 Action安装了插件之后,IntelliJ IDEA 在启动时会自动加载 ~/.ideavimrc
这个配置文件,改动该文件后可以使用如下方式手动重新加载:
:source ~/.ideavimrc
或者直接在编辑器中 :e ~/.ideavimrc
然后在右上角的地方会出现重新 Load 的图标,点击即可让 Idea 重新加载。
我映射了 leader
+ o
打开最近项目列表,用来快速的打开项目。
首先创建一个 keymap
(用过 Vim 的都知道,可以自定义一个 modifier key,通过这个修饰键可以形成一套新的快捷键组合):
let mapleader = ","
nnoremap <Leader>o :<C-u>action RecentProjectListGroup<CR>
然后使用配置的 leader 快捷键 , + o 就可以快速弹出最近打开项目,使用模糊搜索就可以快速打开新的项目。
在不知道这个方法以前,我都是在 [[Alfred]] 中配置了一个 Workflow 来打开新的项目的。在发现上面这个方法后,发现在 IDE 内通过这个方式打开别的项目,远比 Alfred 中要快。熟悉一段时间之后,甚至可以不用看搜索结果,直接使用逗号加 o
然后快速输入项目的模糊查询的关键字,然后回车。
IDEA 自身就提供了非常多的快捷来在代码之间跳转,比如:
在我的工作流里面,为了方便记忆,统一使用 g
作为简记符(表示 go)。比如 gd
表示 go to definition
。
在 .ideavimrc
文件中,定义 map xxx :action yyy
表示自定义一个 keymap
调用 IntelliJ 的 action。
nnoremap gd :action GotoDeclaration
这里的 GotoDeclaration
是 IntelliJ 的一个 action,一个 IntelliJ 的 Action 对应着 IntelliJ 的一个功能。上面的定义就表示在 Normal 模式下定义新的 keymap gd
,表示的是在 Normal 模式下,按下 gd
就会执行 IDEA 的 action GotoDeclaration
。
IntelliJ 提供了一系列的 Action 可以使用。
比如我定义了如下的跳转:
" go to somewhere (g in normal mode for goto somewhere)
nnoremap ga :<C-u>action GotoAction<CR>
nnoremap gb :<C-u>action JumpToLastChange<CR>
nnoremap gc :<C-u>action GotoClass<CR>
nnoremap gd :<C-u>action GotoDeclaration<CR>
nnoremap gs :<C-u>action GotoSuperMethod<CR>
nnoremap gi :<C-u>action GotoImplementation<CR>
nnoremap gf :<C-u>action GotoFile<CR>
nnoremap gm :<C-u>action GotoSymbol<CR>
nnoremap gu :<C-u>action ShowUsages<CR>
nnoremap gt :<C-u>action GotoTest<CR>
nnoremap gp :<C-u>action FindInPath<CR>
nnoremap gr :<C-u>action RecentFiles<CR>
nnoremap gh :<C-u>action Back<CR>
nnoremap gl :<C-u>action Forward<CR>
我使用 t
加上一个字母作为 Toggle 动作的开始方便记忆(t 就表示 toggle)。
比如下面的第一条的 ta
,表示的就是 Toggle Annotate
,在 IDEA 主编辑区域经常看这行代码是谁提交的,那么会使用右击序号空白处,然后选择 Annotate,这个操作可以简化成直接在 Vim 模式的阅读模式下按下 ta
。
一些其他的定义可以参考:
nnoremap ta :action Annotate<cr>
nnoremap tb :action ToggleLineBreakpoint<cr>
nnoremap tm :action ToggleBookmark<cr>
nnoremap tp :action ActivateProjectToolWindow<CR>
在安装 IdeaVim 之后,可以在 normal
模式下使用如下命令查看 IDE 支持的 action:
:actionlist [pattern]
如果要搜索对应的 action 可以直接加上模糊词来搜索,比如 :actionlist declaration
来搜索相关的内容。
执行 action
:action {name}
比如执行 :action Debug
在 ~/.ideavimrc
文件中可以给 Action 其名字,比如
command! Reformat action ReformatCode
在 action 后面的 ReformatCode
是一个合法的 ActionName,通过上面的语句就重新起了一个新的名字叫做 Reformat
。这样就可以通过 :Reformat
来调用。
使用空格加 hl 来切换标签页
nnoremap <space>h gT
nnoremap <space>l gt
Vim 的命令 :e
, :sp
, :vsp
是支持的。
也可以直接使用快捷键 <C-W>s
, <C-W>v
, <C-w>c
来实现对编辑器的分屏。
<C-W>w
可以快速在不同的 Panel 之间切换。
我想要达到的效果是和我在终端中使用 Tmux+Vim 类似,使用 Ctrl+h/j/k/l 来进行分屏。
首先要到设置中把可能的快捷键冲突解决,比如 Ctrl+H 原来被我映射成 Find in Path,现在我使用 gp
(go to path) 作为快捷键触发。
然后将 ,
作为 Leader 快捷键。
" screen management
" Vertical split screen
nnoremap <Leader>\ <C-W>v
nnoremap <Leader>- <C-W>s
nnoremap <C-h> <C-W>h
nnoremap <C-l> <C-W>l
nnoremap <C-j> <C-W>j
nnoremap <C-k> <C-W>k
这样就实现了 IDEA 内部的快速分屏。
记得去设置中将 Ctrl + h/j/k/l 的默认快捷键移除,否则可能会有冲突。
更多的配置可以参考我的 dotfile 配置。
启用 surround 插件来模拟 surround
set surround
在了解了简单的 Vim,并知道 Vim 的能力之后就可以做到 Vim Everywhere,比如
这样在日常、工作的大部分时间中都可以完美的使用 Vim。
[[idea-plugins]]
category: [[IntelliJ IDEA]] [[编程工具]] [[学习笔记]]
远在移动互联网还没有那么发达的今天,Google 曾经收购过一家图片管理与分享的网站叫做 Picasa,Picasa 同时提供了一个跨平台的照片管理工具 Picasa Desktop,用这个工具不仅可以非常方便的管理本地的图片文件,也可以非常方便的分享到 Picasa Web 上,然而随着移动互联网的到来,以及 Google 的转型,Picasa 的服务在 Google 变得没有那么重要,随即在 2016 年停止了服务,我的图片管理也被迫迁移到了 Google Photos。然而一切都开始变得不方便,Google Photos 内自动备份的照片,相册开始无法管理,并且 Google 停止了桌面版的开发,同样使得在桌面上管理图片变得困难,这些年来尝试了 Lightroom,TagSpaces 等等工具,都没有找到特别舒心的。
我的需求其实也并不复杂:
我带着这一些需求一直在寻求一款合适的工具,直到有一天在 Twitter 上有人有相同的需求时,一则评论吸引了我的注意,打开 Eagle 官网 的同时我就被吸引了,一张软件界面的截图直接了当的说明了这个软件的功能,并且几个关键字,「收集」,「整理」,「搜索』,似乎让我一下子想到了 Picasa。更加了解之后才发现,原来 Eagle 不止图片管理这么简单,音频,字体,GIF 管理等等让我迫不及待地下载进行尝试。 安装后 Eagle 会引导安装浏览器扩展,这个扩展可以用来收藏图片,包括拖拽,Alt+ 右击,右键上下文菜单,甚至可以直接批量一键导入网页图片,或者将整个网页保存为图片,或者剪切部分网页。这部分内容扩展的页面 已经给出了非常详细的说明,这里就略过了。
我个人的习惯是每次旅行或外出回来时,会新建一个以日期开头加上地点的文件夹,并导入当前拍摄的照片,然后保证无误后格式化 SD 卡。然后使用 Lightroom 对这个新导入的文件进行一轮的筛选,使用 1~5 快捷键给照片打分,然后对打分的照片进行简单的快速处理。如果有 4 或 5 分的照片,会更进一步微调。然后对有打分的照片进行导出分享。
在遇到 Eagle 之前,我接触的大部分图片都是在浏览器中,所以在浏览器中安装了一个叫做 Imagus 的工具,当鼠标悬浮在图片上后会放大缩略图,并且使用 Ctrl+s 就可以直接下载该图片。但这样的问题便是所有的图片都在 Downloads 文件夹下和其他格式的文件混在一起。等到真正需要使用的时候,搜索变得非常不方便。
而有了 Eagle 之后这个工作流程更加简单了,鼠标拖拽图片即可。
虽然我不是学设计的,但是大学的时候曾经做过一段时间和字体相关的事情,所以也对字体有一些要求,所以如果阅读器支持更换字体,肯定会换成自己喜欢的方正北魏楷书或方正宋三。曾经下载过很多的字体进行对比,如果当时有 Eagle 就方便很多,直接将字体导入软件就能预览文本,再不用现在文本编辑器中编辑一段对话,然后再挨个更换字体了。
不管是在 Picasa,还是在 Lightroom 个人的使用习惯是管理工具管理的内容,在磁盘上实际的位置还是原来的位置,不管是编辑了图片或者是移动了图片都是在本地文件中的修改,然而虽然 Eagle 是一个离线可用的图片管理工具,但一旦图片文件夹导入到 Eagle ,再去磁盘中查看时文件的结构就变成了 Eagle 处理过的扁平的样子,并且会冗余两份图片,一份在原来的位置,一份在 Eagle 中,即使之前存在目录结构也被打散了。一旦文件导入到 Eagle 再想要使用文件管理器来管理这些图片变的不可能。当然这个只是个人的习惯问题。
要再想恢复到原来的文件结构可以右击文件夹,选择导出。
另外一点令我顾虑的就是,一旦使用 Eagle 来管理我的大量图片,这就使我锁定在了 Eagle,让我有一点介意的就是当我试用期过了之后,我竟然无法导出我的所有图片,虽然这些图片都在本地,但是因为已经被 Eagle 重新命名过所以几乎无法直接使用,只能让我用虚拟机再导出一遍。这也加重了我的顾虑。
再以缺点就是在我使用的过程中,有两次无法退出,即使使用强制退出,也无法退出,甚至让我在重启电脑的时候都卡在去停止 Eagle 的地方,我从没在 Mac 上遇到软件强制退出都不行的情况。
综上所有的优点,缺点,我仔细思考了一下,Eagle 确实在某些方面做的不错,对于设计师,或者对于刚刚开始整理素材的人来说是一个非常不错的选择,而一旦已经有了自己的素材库的人来说,不可避免的需要将素材导入到 Eagle,而这是一个不算愉快的选择,并且一旦想要放弃 Eagle,那么将数据导出便又是一段不愉快的旅程。所以最后再试用期满后,我迫不得已按了一台虚拟机导出整个库后删除了 Eagle。