迟到的「给编程初学者的一封信」

这些天翻箱倒柜,翻出来一些大学时候的文档,其中一篇是当时上外教课时打印给我们的材料,虽然可以看得到当时也在上面做过笔记,但现在已经完全不记得有这样一份文档的存在了。但回过头再看文档的内容,每一句话都是非常珍贵的建议,是一份那个时期完全需要读一下的材料,但当时却并没有好好珍惜。这份文档的名字叫做「An open letter to those who want to start programming」,如果记得没错的话这份文档交到我手上的时候,应该就是我刚去学习如何编程的时候。如果当时就能够理解这一份文档内容的话,这些年来我肯定能少走不少的弯路。

在结合到最近陆陆续续看过关于认知的一些书,才发现我自身的问题并不是在于看得东西不够多,而是没有完全理解我曾经看过的东西。就如这一份文档那样,现在的时间来看文档上列举的内容都是当下非常好的建议,然而当时的我并不理解,所以自然而然就忘记了这一份文档上的建议。

先来总结一下这一份文档中作者给出来的几个建议:

  • starting creating something just for fun. 你需要毫不犹豫的开始做一些事情,而不是纠结于先开始学习,或者设置编辑器。
  • Initially, screw the algorithms and data structures. 开始的时候不要在意算法和数据结构。当然作者并不是说算法和数据结构不重要,而是在起初的时候,不要在意算法和数据结构的细节,而是在学习和尝试的过程之中体会它们的作用,记住算法和数据结构的名字,当真正要用到的时候深入了解,并切身体会它们的具体应用场景。当你真正需要他们的时候,可以用自己的熟悉的语言实现。
  • Choose a good language. 选择一门你觉得可以在短时间内通过它生产出内容的语言。当完成一个任务并看到产出才是激励继续的动力。
    • 不要选择那种需要笨重的 IDE 才能运行的语言,迅速的产生成果才是唯一的目标
  • Choose a good editor, 编辑器之于程序员就相当于弓之于弓箭手。
    • Vim,许多 Linux 发行版的默认编辑器,虽然入门和学习曲线比较陡峭,但是是一个学习了终身收益的工具
    • Emacs,同样有着陡峭的学习曲线,但同样强大
  • Use an operating system that’ll teach you something,选择一个可以学习到的操作系统。目前主流的桌面操作系统也只有 Windows,Linux 和 macOS,建议你选择一个合适的 Linux 发行版,作者的观点是 Windows 不会教你任何事情,但是使用 Linux 你能收获更多。我曾经花费了一段时间将全部的资料和应用切换到 Linux Mint,我也使用这个发行版大约 6 年左右了,虽然确确实实会遇到一些问题,但正是解决这些问题的过程,让我从中学习到了很多东西,比如 Linux的启动过程Linux 下开机启动应用脚本,以及关于磁盘的配置网络的配置 等等。
  • Don’t copy-paste files to backup stuff. 作者在这边推荐了 Git,而当年我们在学校学习的还是 SVN,真正将 Git 运用到方方面面我也是这些年来才真正实施的,以前只是拿来作为代码的版本管理,而之后我用 Git 来管理任何可见的文本,比如博客,比如笔记,比如文本配置。再结合 git worktreegit subtree 等等的功能,可以在任何一台电脑上迅速恢复我的工作环境。
  • Know where to get help. 知道如何寻求帮助,社群,StackOverflow,IRC,都是可以获取帮助的渠道,而对于我真正学会使用 StackOverflow 则是要到好几年之后独自学习 Cocos-2x 的时候,因为参考资料少,官方文档也不全面,StackOverflow 帮助了很多,那个时候我才理解到互联网的互帮互助是以这样的方式来实现的,那以后也曾经花了一段时间去积极回答别人的问题。
  • Develop your netiquette. 遵守网络礼仪,学会如何提问,好好地阅读一下提问的哲学对我来说又是在读完这篇文档之后好几年才真正理解的。
  • Meet people, because books only teach you routine stuff. 你不是这个领域唯一的程序员,交一些朋友和他们一切完成一些事情,你可以学到比图书更多的东西。这些年看技术书籍除非一些比较基础的算法大部分的书籍都会在出版之后不久便过时,但是 People 会随着时间改变。
  • Writing open source code. 编写开源代码是一项回馈,甚至可以说是一项慈善。如果代码有用,别人回来使用或者修正你的代码。你也可以通过其他人添加的代码和提交的修改建议来学习并巩固你的编程技能。为开源项目编写代码不需要一个巨大的项目,你可以从最小的代码开始,比如一段下载 YouTube 视频的代码。当我看到这一段代码的时候,正好发生了 GitHub 下架 youtube-dl 项目,之后经过复杂的程序又恢复的事件,原始作者也正好出现来讲述了 youtube-dl 这个项目诞生的背景。[[202011032314-GitHub-take-down-youtube-dl]]

文中的内容大部分内容都在我之后对编程这个领域进一步的认识之后,陆陆续续才发现这每一条建议的可贵之处。我不知道是不是每个专业都会有这样一封写给初学者的信,但无疑一个专业领域前辈所写的内容对后来者的启发意义都是巨大的。

附录

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…

  • SublimeText 2 – recommended if you are just starting.
  • Emacs – huge learning curve. Complex key shortcuts. And to be able to customize it, you’ll need to learn Emacs Lisp.
  • Vim – used by many for it’s simplicity and the fact that it comes with linux distros by default. I used Emacs for 2yrs and then switched to Vim to run away from emacs’s complex key strokes and when my little finger on both hands started hurting. Knowing vim keystrokes is a must. When you work remotely and try to type out code on some server from your computer, you’ll know that the only editor available from the command line without any installs, is Vim.

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


2021-01-02 programming , coding , editor , linux , learning , suggestion , open-letter

2020 读书记录

2020 年注定是会在历史上留下不平凡的一年,年初的疫情到年底问题依然还在,而且在不确定疫苗的情况,在加上英国的病毒变异情况,不清楚到什么时候才是结束。再回头看今年的大事小事,从年初的李文亮事件,到年末的蛋壳,以及阿里被禁止上A股,有些事情发生地太突然,来不及思考,但只有思考,不仅是在事前的还是事后的思考都有其价值。

李文亮事件

李文亮因言获罪,而密尔早在两百年前就曾经说过,即便是荒谬的言论也不得限制,对该言论的讨论过程能进一步证明相反观点的价值。而李文亮事件就是典型的「被压制的言论包含部分的真理」的情况,而正是因为言论的被压制,终究封城是阻止不了病毒的扩散的。而在看美国宪法,以及关于[[宪法第一修正案]]关于言论自由边界的书,能看到为了维护不进行事先审查言论的权利,经过了多少的争论。

而换一个角度来看,来看看一位金融从业者在[[非对称风险 笔记]]中的观点,「持续暴露在小概率风险下,即使爆仓风险的概率小到万分之一,那么在持续、重复的过程中爆仓的概率会越来越大」。相同的观点套用到现实,能禁止一个人说话,能阻止一群人发表观点,但只要危机没有被解决,终究会造成无法挽回的损失。

金融相关

  • [[黑天鹅]],什么叫黑天鹅,[[塔勒布]] 总结了三点,意外性、极端影响、事后的可预测性。塔勒布让我们留意生活中那些未知的和在我们认知中不可能的事物,而不是一直在讨论琐碎的已知的和重复发生的事物。
  • 非对称风险,一书在讲述一个「风险共担」的理念,举一个最简单的例子,以前降落伞的安全程度很低,所以订购商在验货的时候让制造商穿一件从空中跳下来,于是安全系数就提升了。这是最显而易见的风险共担。塔勒布从风险控制,将其理论拓展到经济,商业,社会生活的方方面面。作者提倡将做出决定和承担后果二者相互平衡的机制。
  • 随机漫步的傻瓜,谨小慎微的投资,学会有[[安全边际]]的[[价值投资]]
  • 牛津通识读本:全球经济史,从资本主义诞生开始,讲述全球这个地区的经济发展道路
  • 牛津通识读本:资本主义,这是一本讲述资本主义起源,解释什么是资本主义,以及资本主义在发展的过程中自身的演变的书。

法律

金斯伯格大法官离世后,先是让我认识了这位饱受尊敬的高龄大法官,然后将我带回了最高法院,在最高法院发展的几百年历史中,让我认识到了自由派的[[浪漫主义]]者 [[霍姆斯大法官]],也让我了解了未曾当过大法官却深刻影响了最高院的 [[勒尼德·汉德]] 法官,以及到更近代的第一人女性大法官[[奥康纳大法官]],当代的自由派[[苏特大法官]],以及许许多多以前可能听过但不曾有印象的名字,这些躲在背后的大法官们在一件件的案件,以及对这些案件的判决中,形象更加丰满起来。

  • 温柔的正义 : 美国最高法院大法官奥康纳和金斯伯格如何改变世界,这本书主要介绍了最高法院历史上的两位女性大法官的故事。
  • 九人,以人物传记的形式记录了最高法院从1990年到2007年17年间的人事更替、提名过程、立场变迁与判决内幕,内容跨越伯格法院、伦奎斯特法院和罗伯茨法院三个时期。
  • 牛津通识读本:美国总统制,讲述了美国总统制,以及行政分支
  • 牛津通识读本:托克维尔,托克维尔的人物传记
  • 大法官说了算,通过具体的历史事件来记录了最高法院权威性的由来,以及最高法院在这些年来对直播,互联网言论,死刑等等问题的观点
  • 牛津通识读本:法哲学,什么是法律,法律是否能维护权利,保障公平等等问题
  • 美国宪政历程,这是一本非常推荐的书,看了上面这么多书,唯有这一本读起来最舒服,作者[[任东来]]先生也说过这是写给普通人看的科普读物。但任先生通过书中的二十多个案例展现了一部形象的宪法历史。
  • 牛津通识读本:美国政党与选举
  • [[联邦党人文集]],今年二次阅读这一本书,初读的时候,因为没有了解当时的社会环境,也没有对其中例如 [[詹姆斯 麦迪逊]]、[[亚历山大·汉密尔顿]] 有所了解,但今年结合一部音乐剧《汉密尔顿》,又重新加深了对汉密尔顿的认识,以及上面提及的诸多哲学,法律的著作中都在引述这本书中的内容,更进一步认识了这一本书的价值所在。读完这本书还还好奇为什么建国之初的联邦党到如今却消失了,还特意查了一番。

历史

如果说上半年收获最大的作者是 [[塔勒布]] 的话,那么下半年收获最大的作者便是 [[弗朗西斯·福山]],早在去年就看过其[[The Origins of Political Order]],但是碍于当时的认知和环境,实际上并没有完全读懂,今年先读了其之前的著作 [[历史的终结与最后的人]] ,然后再看其更进一步的论述 [[The Origins of Political Order]],才理清了其脉络。

  • [[历史的终结与最后的人]]
  • [[The Origins of Political Order]]
  • 政治秩序与政治衰败

其他

  • 如何阅读一本书,今年年中收获的一本书,以往我都不屑于这一类 How to 的书,但今年重新发现,方法的重要性。How to read a book, How to take smart notes 是今年两本对我影响比较大的书,加上 ObsidianZettelkasten 的大量使用,不仅让我提升了阅读的乐趣也是的我今年的笔记效率大幅度提升。然后再回顾过去的阅读笔记,发现我大量的时间都只花在了 inspectional reading,也就是所谓的粗读中,连基本的分析阅读都未能做到。
  • 论自由,重读论自由,密尔这位自由主义的奠基人,两百多年前的声音在如今依然振聋发聩,「那些被压制的言论可能包含着为社会所需要的部分或全部的真理。即便一个荒谬的信仰也是有价值的,因为进行争论的过程也是有价值的。因为对其进行争论的过程本身即可以检验和进一步证明相反观点的真实性。」
  • [[韩剧如何讲故事]],很早就在豆瓣上看到的一本书,作者是写个编剧阅读的,而我作为一个门外汉,从侧面看韩国综艺和韩剧这些年的发展,不难看出他们对故事性的追求,他们在发展中进步,在进步中进一步的扩展自己讲故事的能力,韩国在媒体领域里面的创作和创新值得我好好的学习。另外一个让我不由的串起来的便是,[[托克维尔]] 说社会的自由依赖与[[公民自治]],形成大大小小的公民团体谋取利益,而恰恰是在这样一本讲述编剧的故事中,我发现韩国有自己的编剧协会,每一年都会将刚入行的编剧送到这个领域去学习,比如去医院,法院,监狱等等平常人难以进入的场所进行取材,再回想到这些年韩剧中不乏那些描写细腻的医疗剧,法庭辩护,以及监狱生活的剧,一下子就能看到其中的关联。
  • 韩国综艺如何讲故事,Running Man 开启了我对韩国综艺的认识,[[无限挑战]]将我对综艺的认识提升到另外一个境界,我本不看任何形式的综艺,但我发现原来搞笑,甚至有些无厘头的综艺中也能塑造一代人的认识。这又让我想起了去年去首尔,去柜台结帐的时候,老板正在看当时已经停播的无挑选举特辑,一下子就认识到即使做的只是放送局最基础的搞笑综艺,只要能够用心,能够讲好故事,依然能够让最最普通的人感同身受,甚至影响现实世界。如今无挑已经不在,但变成了刘在石一个人的挑战,虽然现在并没有每一期都看,但如果有空闲的时间总会想起。
  • 饱食穷民,一本讲述日本经济的纪实文学,其中描述了盘踞街头的小微贷,无法抑制的[[消费主义]],不断延长的工作时间,人们被改造成了金钱和物质统治下的行尸走肉,深陷其中无法自拔。这个故事像极了今天的中国,小微贷变成了移动互联网各家争夺的地盘,自我启发变成了企业培训,这种网课,而现在广为所知的996,也只不过是日本员工日夜拼命的缩影。
  • [[软技能]]

2020-12-27 reading , book

logback.xml 配置详解

之前两篇文章简单的介绍了 [[Logback]] 是什么,以及基本的使用,这一篇文章着重说一下 Logback 中最重要的 logback.xml 配置文件的编写。

![[Pasted image 20201210145047.png]]

配置文件格式:

<configuration>
    <appender>    //输出到控制台的信息配置
       //....
    </appender>

    <appender>   //输出到info文件的配置
       //...
    </appender>

    <appender>   //输出到error文件的配置
    </appender>

    <logger>     //特殊处理日志定义
        //..
    </logger>

    <root level="debug">  //总日志开关
        //...
    </root>
</configuration>
  • appender 配置打印行为,比如将日志输出到控制台,文件,数据库,网络端口等等
    • 可以配置多个 appender
  • logger 用来处理特定的包和类

根节点

根节点 <configuration> 包含三个属性:

  • scan: 为 true 时,如果配置改表会重新加载,默认是 true
  • scanPeriod: 检测配置文件修改的时间间隔,默认单位毫秒,默认时间间隔 1 分钟
  • debug: 为 true 时,打印 logback 内置日志信息,实时查看 logback 运行状态,默认 false

例如:

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!--其他配置省略-->
</configuration> 

子节点

contextName 子节点

子节点 <contextName>,设置上下文名称。

<configuration scan="true" scanPeriod="60 seconds" debug="false">
     <contextName>myAppName</contextName>
    <!--其他配置省略-->
</configuration>

property 子节点

用来定义变量值,两个属性 namevalue,通过 <property> 定义的值会被插入到 Logger 上下文中,可以使用 ${} 来使用。

<configuration scan="true" scanPeriod="60 seconds" debug="false">
   <property name="APP_Name" value="myAppName" />
   <contextName>${APP_Name}</contextName>
   <!--其他配置省略-->
</configuration>

timestamp 子节点

获取时间戳字符串

  • key:标识此 timestamp 名字
  • datePattern:设置当前时间,遵循 java.text.SimpleDateFormat
<configuration scan="true" scanPeriod="60 seconds" debug="false">
  <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
  <contextName>${bySecond}</contextName>
  <!-- 其他配置省略-->
</configuration>

appender 子节点

appender 是负责写日志的组件,两个属性:

  • name,指定 appender 名字
  • class,指定 appender 全限定名

logback 实现了一些内置的 appender,可以将日志输出到控制台,文件等等地方。

<appender name="file-appender" class="ch.qos.logback.core.rolling.RollingFileAppender">
  ....
</appender>

内置的 appender:

  • ConsoleAppender
  • FileAppender
  • RollingFileAppender

ConsoleAppender 控制台输出

将日志输出到控制台(Console),两个子节点:

  • <encoder>: 对日志输出格式化
  • <target>: 字符串 System.outSystem.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

将日志输出到文件(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>

RollingFileAppender

滚动日志,记录日志到文件,当符合某条件时,将日志记录到其他文件。子节点:

  • <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.logmylog-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>

AsyncAppender

异步日志

<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 具体可以参考官方文档

  • SocketAppender
  • SMTPAppender
  • DBAppender
  • SyslogAppender
  • SiftingAppender

logger 子节点

用来设置某一个包或具体的某一个类的日志打印级别,执行 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>

reference


2020-12-10 logback , spring , logger , log , logging , slf4j , java , java-log

Logback 使用

Logback 在之前的文章中说过,是一个开源日志组件。

[[Logback]] 推荐和 SLF4J 一起使用。

Maven 依赖

<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

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 成功了");
    }
  }

2020-12-10 logback , slf4j , spring , java , logger

Logback 基本使用介绍

[[Logback]] 是 log4j 的创始人开发设计的另一个开源日志组件,期望成为 log4j 项目的继任者,相较于 log4j,Logback 有一些优势

  • 更快的实现,更小的内存占用
  • 充分的测试,Extensive battery of tests
  • 可以直接使用 [[SLF4J]] 接口
  • 健全的文档
  • 可以通过 XML 或 Groovy 进行配置
  • Logback 可以在配置更改后自动加载
  • 可以优雅的从 IO 失败中恢复
  • 自动移除老的日志存档
  • 自动压缩存档日志文件
  • 在 Prudent mode 模式中,在不同 JVMs 中运行的不同 FileAppender 实例可以安全地写到同一个文件中
  • Lilith,Log 事件的观察者
  • Conditional processing of configuration files
  • Filters,过滤器,要诊断问题,在 Log4j 中,只有降低日志级别才会打印日志,但这样会打出大量的日志,影响性能,但是 Logback 可以继续保持日志级别,而单独添加一个过滤器,比如过滤某用户的登录日志,将其打印在 DEBUG 级别,而其他用户的日志继续打印在 WARN 级别,可以参考 [[MDCFilter]]
  • SiftingAppender,多功能的 Appender,可以根据给定的参数对日志文件进行切分,比如根据用户的 ID 进行切分, 每个用户都会有一个日志文件
  • Stack traces with packaging data
  • Logback-access

模块

Logback 划分成三个模块:

  • logback-core 基础模块
  • logback-classic 这是 Log4j 的改良版本,完整实现了 slf4j,可以很方便的切换成其他日志系统比如 log4j 或 JDK Logging
  • logback-access 访问模块和 Servlet 容器集成,提供 HTTP 访问日志

Maven 依赖:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>${logback.version}</version>
</dependency>

logback 配置

  • Logger 是日志的记录器,关联到应用的 context ,用于存放日志对象,可以定义日志级别、类型
  • Appender 负责指定日志输出的目的地,可以是控制台,文件,远程套接字,MySQL,PostgreSQL,Oracle 等数据库,JMS 和远程 Unix Syslog 守护进程等等
  • Layout 负责把事件转换成字符串,格式化日志信息的输出

默认配置

应用在启动时按如下的顺序寻找配置文件:

  • classpathlogback-test.xml 文件
  • logback.groovy
  • logback.xml
  • 上述文件都找不到,JDK 的 SPI 机制查找 META-INF/services/ch.qos.logback.classic.spi.Configurator 中的 logback 配置实现类
  • 如果都不成功,则使用自带的 BasicConfigurator 来配置,并将日志输出到 console。

配置文件 logback-test.xmllogback.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>

Logback 配置文件

  • configuration 开头,后面有 0 个或多个 <appender> 元素,0 个或多个 <logger>,最多有一个 <root> 元素。

[[2020-12-10-logback-xml-config]] [[2020-12-10-logback-usage]]

文档


2020-12-10 logback , spring , java , logger , slf4j

使用 mdBook 生成在线文档

很早以前就用过 [[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

要生成页内目录可以使用 toc

cargo install mdbook-toc

相关

Python 的文档工具 [[mkdocs]] :

tag: #GitBook #wiki #Book #Markdown #note #writing #个人知识管理


2020-12-03 gitbook , online-document , rust , wiki , markdown , notes , static-site

我的 IntelliJ IDEA Vim 插件配置

IdeaVim 是 IntelliJ IDEA 编辑器下一款模拟 Vim 模式及快捷键的开源插件。鉴于大部分的时间都在 IntelliJ IDEA 下工作,所以总结一下在 IDEA 下使用 Vim 的一些快捷方式。

阅读完本文之后,你可以

  • 在 IntelliJ IDEA 下完全通过键盘操作完成 99% 的操作
  • 可以通过组合按键实现任意自己想达到的操作
  • 保持在多个平台(macOS,Linux,Windows) 下操作一致

为什么要用 IdeaVim

  • 既充分利用了 IntelliJ 提供的代码补全,重构,代码浏览等等功能,又可以充分利用 Vim 的多模式,以及 Vim 在编辑器中的高效操作
  • 利用 ~/.ideavimrc 来复用 Vim 的工作方式,即使将工作环境切换到 Terminal 下也可以沿用,并且充分利用 Idea 提供的 Action
  • 多平台下沿用一套工作流程,不需要解决因为操作系统不同而产生的快捷键冲突

必须安装的插件

利用 .ideavimrc 配置 IdeaVim

重新加载 .ideavimrc

安装了插件之后,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 然后快速输入项目的模糊查询的关键字,然后回车。

Go to Declaration

IDEA 自身就提供了非常多的快捷来在代码之间跳转,比如:

  • 跳转到变量,方法,类的定义
  • 跳转到 Super 类
  • 跳转到方法被调用的地方
  • 跳转到实现的地方
  • 跳转到文件
  • 跳转到测试类

在我的工作流里面,为了方便记忆,统一使用 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>

Toggle Actions

我使用 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>

查看 IDEA 支持的 Action

在安装 IdeaVim 之后,可以在 normal 模式下使用如下命令查看 IDE 支持的 action:

:actionlist [pattern]

如果要搜索对应的 action 可以直接加上模糊词来搜索,比如 :actionlist declaration 来搜索相关的内容。

执行 action

:action {name}

比如执行 :action Debug

重新命名 Action

~/.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 配置。

两个比较有用的快捷键

  • ⌘+F12 ActivateTerminalToolWindow 在 Linux 下设置成 Alt+F12 可以快速调用或隐藏 IDEA 内置的终端
  • ⌘+⇧+F12 HideAllWindows 最大化编辑器,隐藏所有其他窗口

Cmd 组合

  • Cmd+a/c/v/x/z
  • Cmd+w 关闭当前的文件
  • Cmd+e 最近的文件
  • Cmd+t Refactor this 重构
  • Cmd+n Generate 快速生成模板代码
  • Cmd+o File Structure 当前文件的文件结构
  • Cmd+b 跳转到定义
  • Cmd+[/]

surround

启用 surround 插件来模拟 surround

set surround

在了解了简单的 Vim,并知道 Vim 的能力之后就可以做到 Vim Everywhere,比如

  • 在浏览器中安装 Vimium 插件之后可以在浏览器中使用 Vim 的浏览,搜索等
  • 在 Obsidian 中开启 Vim 模式,也可以自定义使用 Vimrc 配置文件

这样在日常、工作的大部分时间中都可以完美的使用 Vim。

reference

[[idea-plugins]]

category: [[IntelliJ IDEA]] [[编程工具]] [[学习笔记]]


2020-12-02 idea , vim , vimrc , config , vim-config , jetbrain , intellij-idea , ide

图片管理工具 Eagle

远在移动互联网还没有那么发达的今天,Google 曾经收购过一家图片管理与分享的网站叫做 Picasa,Picasa 同时提供了一个跨平台的照片管理工具 Picasa Desktop,用这个工具不仅可以非常方便的管理本地的图片文件,也可以非常方便的分享到 Picasa Web 上,然而随着移动互联网的到来,以及 Google 的转型,Picasa 的服务在 Google 变得没有那么重要,随即在 2016 年停止了服务,我的图片管理也被迫迁移到了 Google Photos。然而一切都开始变得不方便,Google Photos 内自动备份的照片,相册开始无法管理,并且 Google 停止了桌面版的开发,同样使得在桌面上管理图片变得困难,这些年来尝试了 Lightroom,TagSpaces 等等工具,都没有找到特别舒心的。

我的需求其实也并不复杂:

  • 管理本地大量的单反照片,添加标签以及其他 meta 信息,可以方便我快速定位、查找,这一点 Lightroom 做的不错,但 Lightroom 有些笨重
  • 集中管理我所有的照片,图片,让我可以快速预览照片
  • 我希望我的图片库可以随时随地可以访问

我带着这一些需求一直在寻求一款合适的工具,直到有一天在 Twitter 上有人有相同的需求时,一则评论吸引了我的注意,打开 Eagle 官网 的同时我就被吸引了,一张软件界面的截图直接了当的说明了这个软件的功能,并且几个关键字,「收集」,「整理」,「搜索』,似乎让我一下子想到了 Picasa。更加了解之后才发现,原来 Eagle 不止图片管理这么简单,音频,字体,GIF 管理等等让我迫不及待地下载进行尝试。 安装后 Eagle 会引导安装浏览器扩展,这个扩展可以用来收藏图片,包括拖拽,Alt+ 右击,右键上下文菜单,甚至可以直接批量一键导入网页图片,或者将整个网页保存为图片,或者剪切部分网页。这部分内容扩展的页面 已经给出了非常详细的说明,这里就略过了。

picasa screenshot

我个人的习惯是每次旅行或外出回来时,会新建一个以日期开头加上地点的文件夹,并导入当前拍摄的照片,然后保证无误后格式化 SD 卡。然后使用 Lightroom 对这个新导入的文件进行一轮的筛选,使用 1~5 快捷键给照片打分,然后对打分的照片进行简单的快速处理。如果有 4 或 5 分的照片,会更进一步微调。然后对有打分的照片进行导出分享。

关于收藏图片

在遇到 Eagle 之前,我接触的大部分图片都是在浏览器中,所以在浏览器中安装了一个叫做 Imagus 的工具,当鼠标悬浮在图片上后会放大缩略图,并且使用 Ctrl+s 就可以直接下载该图片。但这样的问题便是所有的图片都在 Downloads 文件夹下和其他格式的文件混在一起。等到真正需要使用的时候,搜索变得非常不方便。

而有了 Eagle 之后这个工作流程更加简单了,鼠标拖拽图片即可。

字体管理

虽然我不是学设计的,但是大学的时候曾经做过一段时间和字体相关的事情,所以也对字体有一些要求,所以如果阅读器支持更换字体,肯定会换成自己喜欢的方正北魏楷书或方正宋三。曾经下载过很多的字体进行对比,如果当时有 Eagle 就方便很多,直接将字体导入软件就能预览文本,再不用现在文本编辑器中编辑一段对话,然后再挨个更换字体了。

eagle font manage

常用的快捷键

  • ⌘+⇧(Shift)+n 创建文件夹
  • ⌘+t 增加标签
  • ⌘+Alt+x
  • ⌘+j 在不同的文件夹之间切换
  • ⌘+⇧(Shift)+j 将指定图片移动到指定文件夹
  • F 弹出分类对话框,可以快速进行分类

几个独特有有用的功能

  • 按颜色过滤素材

缺点

不管是在 Picasa,还是在 Lightroom 个人的使用习惯是管理工具管理的内容,在磁盘上实际的位置还是原来的位置,不管是编辑了图片或者是移动了图片都是在本地文件中的修改,然而虽然 Eagle 是一个离线可用的图片管理工具,但一旦图片文件夹导入到 Eagle ,再去磁盘中查看时文件的结构就变成了 Eagle 处理过的扁平的样子,并且会冗余两份图片,一份在原来的位置,一份在 Eagle 中,即使之前存在目录结构也被打散了。一旦文件导入到 Eagle 再想要使用文件管理器来管理这些图片变的不可能。当然这个只是个人的习惯问题。

eagle folders

要再想恢复到原来的文件结构可以右击文件夹,选择导出。

另外一点令我顾虑的就是,一旦使用 Eagle 来管理我的大量图片,这就使我锁定在了 Eagle,让我有一点介意的就是当我试用期过了之后,我竟然无法导出我的所有图片,虽然这些图片都在本地,但是因为已经被 Eagle 重新命名过所以几乎无法直接使用,只能让我用虚拟机再导出一遍。这也加重了我的顾虑。

再以缺点就是在我使用的过程中,有两次无法退出,即使使用强制退出,也无法退出,甚至让我在重启电脑的时候都卡在去停止 Eagle 的地方,我从没在 Mac 上遇到软件强制退出都不行的情况。

同类型的其他产品

总结

综上所有的优点,缺点,我仔细思考了一下,Eagle 确实在某些方面做的不错,对于设计师,或者对于刚刚开始整理素材的人来说是一个非常不错的选择,而一旦已经有了自己的素材库的人来说,不可避免的需要将素材导入到 Eagle,而这是一个不算愉快的选择,并且一旦想要放弃 Eagle,那么将数据导出便又是一段不愉快的旅程。所以最后再试用期满后,我迫不得已按了一台虚拟机导出整个库后删除了 Eagle。


2020-12-02 software , photo , photo-management , picasa , picture

提升 mysqldump 导入导出 MySQL 的速度

在前端时间网上泄漏出来一个巨大包括了近 8 亿 QQ 账号的绑定电话号码数据库,于是想着导入到本地的 MySQL 看看,提升一下查询的速度,因为这个巨大的绑定关系,即使用 grep 查询也需要花费非常多的时间。

于是我新建了表

CREATE TABLE `qq_bind` (
  `phone` bigint NOT NULL,
  `qq` bigint DEFAULT NULL,
  KEY `ix_qq_bind` (`phone`,`qq`),
  KEY `ix_qq` (`qq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

于是我想当然的想利用 mysqldump 命令来导入文件,但是发现导入的速度非常的慢,并且看到磁盘占用的速度飞速上升。以这样的导入速度,我大致计算了一下得一个多星期才能导入完成。

后来我就想办法怎么才可以提升这个导入的速度,发现如果表上有索引,或者 Primary Key 会大大的影响导入速度,所以:

  1. 移除所有的索引,包括唯一索引,除非能确保导入的数据是唯一的,否则也不建议留着,Primary Key 也是索引。

但是我移除了所有的索引之后,再执行 mysqldump 速度虽然有提升,但依然非常慢。所以不得不找其他办法。

使用 LAOD DATA INFILE 导入文件到 MySQL

再搜寻了一番之后发现 MySQL 可以使用 LOAD DATA INFILE 这样的语句来批量导入数据。

登录 MySQL cli 后可以执行:

LOAD DATA INFILE '/Users/einverne/Downloads/demo.csv' 
IGNORE INTO TABLE demo_table 
FIELDS TERMINATED BY ',' 
ENCLOSED BY '"' 
LINES TERMINATED BY '\n' 
IGNORE 1 ROWS;

或者使用命令行:

mycli -h host -u root -p -D database_name -e "LOAD DATA INFILE '/path/to/file.csv' INTO TABLE demo_table FIELDS TERMINATED BY ','"

说明:

  • Terminated 字段分隔符(列分隔符)。一般是空格或者 \t
  • Enclosed 字段括起字符。没有为空字符即可
  • Escaped 转义字符。没有为空字符即可
  • Terminated 记录分隔符(行结束符)

插入语句:

  • Into Table 代表插入,记录已存在(唯一键约束)则失败不再往下执行。
  • Replace Into Table 代表覆盖,记录已存在则覆盖(是整条记录覆盖,没有列出的字段给默认值)。
  • Ignore Into Table 遇到已存在直接跳过。

LOAD DATA INFILE 原理

LOAD DATA INFILE 比单纯的 INSERT 要快。

  • insert 每次运行,都会更新一次索引,而 load 语句全部执行完才会更新索引。

需要注意的是,当时用 LOCAL 或者 LOAD DATA 时,文件的拷贝会保存到服务器的 temp 目录,这个目录不是由 tmpdir or slave_load_tmpdir 配置决定的,而是操作系统的临时目录 (temporary 目录)。

所以如果 CSV 文件比较大,操作系统临时目录无法放下,可以将文件分割成多份,分批次进行操作。

$ split -l (numbersofrowsinfile / ((filesize/tmpsize) + 1)) /path/to/your/<file>.csv

reference


2020-11-25 mysql , linux , mysqldump , sql , methodology

Mac 应用篇:Hammerspoon 自动化工具使用

这是一篇耽搁了很久,一直躺在我的 Obsidian 笔记中的一篇文章,一直就想好好介绍一下 Hammerspoon,但是因为过去虽然也在用 macOS,但是使用最多的还是 Ubuntu,Hammerspoon 只能在 macOS 上使用,就没有那么大的兴致再花时间学习它的使用。但最近更新了一下系统,发现 Hammerspoon 出了一点问题,没有了 Hammerspoon 之后我才发现很多不适应的地方,那就在花一点时间再梳理一下我的配置。

Hammerspoon 是什么?

Hammerspoon 是一个 macOS 上开源的自动化工具,什么叫做自动化工具呢?通过 Hammerspoon ,可以使用一些脚本来实现原来只能通过界面操作,或快捷键才能达到的效果,并且实现系统自动化。最简单的例子,比如当我连上家里的 WiFi 的时候,就自动将音量调成 3 档;再比如当我切换窗口的时候,自动切换输入法,比如在 IDEA IntelliJ 中自动使用英文输入法,当我打开 Obsidian 则自动切换成中文输入法。

Hammerspoon 使用 Lua 脚本语言与操作系统通信。通过编写 Lua 脚本实现与 macOS API 的交互,Hammerspoon 提供的 API,包括应用的、窗口的、鼠标指针、文件系统、声音设备、电池、屏幕、键盘/鼠标事件、粘贴板、地理位置服务、WiFi 等等。Hammerspoon 是操作系统和 Lua 执行引擎的桥梁,通过 Hammerspoon 可以让 macOS 实现非常强大的自动化。

官网: https://www.hammerspoon.org/

Hammerspoon 能做什么

Hammerspoon 实际上是将 macOS 的系统接口实现了一层转发,让用户可以通过简单的 Lua 脚本进行配置,从而实现一定的 UI 自动化,一旦能够直接从 API 层面对接操作系统,那么 Hammerspoon 能够做的事情就非常多了:

  • 创建并管理全局快捷键,这样就可以创建一组属于自己的快捷键工作流
  • 管理窗口 move windows,结合自定义快捷键可以非常轻松地对窗口进行管理
  • 发送通知 display notifications
  • 和其他应用交互 talk to other applications
  • 添加自定义内容到状态栏 add things to your menu bar,有了这个功能就可以在状态栏上添加任何自定义的内容,比如有人利用公开的天气 API 在状态栏显示最近和未来的天气,可以显示系统的网速等等
  • 监听文件内容变化,可以利用这个自动加载配置,或者自动提交任务等等
  • 监听 WiFi 变化,可以利用不同的网络环境配置不同的网络配置,自动切换不同的工作环境等等
  • 监听应用的启动和停止
  • 在屏幕上绘制 draw on the screen
  • 监听电池变化 watch for changes to your battery
  • 当电脑接口对接硬件时触发动作 carry out actions when you plug things into your computer

Installation

通过 Homebrew 安装:

brew install --cask hammerspoon

上手配置

Hammerspoon 的默认配置在 ~/.hammerspoon/init.lua,我个人通过将配置文件放在 dotfiles 中软链接到目的位置来同步配置。

做一个最简单的例子,在 init.lua 文件中写入:

hs.alert.show("Config reload!")

然后重新加载 Hammerspoon 配置,就会看到在屏幕中央出现 “Config reload!” 的弹出提示。

使用 Hammerspoon 作为管理窗口工具

虽然 [[Mac 上的窗口管理工具]] 有很多,免费的,收费的,Moom, Rectangle 等等,但是自由度都没有 Hammerspoon 多。

下面是我使用的一些窗口管理快捷键。

按下 Option+r 进入窗口的管理模式,在该模式下按下快捷键可以实现非常多的操作:

  • HL/JK 可以用来将窗口按照左、右、下、上分屏
  • ASDW 可以用来移动窗口的位置,对应上下左右
  • Y/O/U/I 可以将窗口变成四分之一屏幕大小,分别是左上,右上,左下,右下
  • Shift+HL/JK 可以收缩窗口大小,比如 Shift+H 就是让窗口右侧往左侧缩小
  • =/- 用来扩展和收缩窗口
  • Left/Right/Up/down 将窗口移动到左边、右边、上边、下边
  • F 全屏
  • C 中央
  • ESC/Q 退出该模式
  • Tab 显示 Cheatsheet

这一套窗口管理方法来自 ashfinal/awesome-hammerspoon

不过我个人最常使用的快捷键还是 Hyper + h/l/j/k 可以将当前的窗口以左/右/下/上方式进行分屏。这里需要结合 [[Mac 应用 Karabiner Elements 键盘自定义工具]]

[[Mac 下的自定义快捷键]]

使用 Hammerspoon 实现 Hyper + h/l/j/k 管理窗口的相关配置:

hyper = {"ctrl", "alt", "cmd", "shift"}
function move_window(direction)
    return function()
        local win      = hs.window.focusedWindow()
        local app      = win:application()
        local app_name = app:name()
        local f        = win:frame()
        local screen   = win:screen()
        local max      = screen:frame()
        if direction == "left" then
            f.x = max.x + 6
            f.w = (max.w / 2) - 9
        elseif direction == "right" then
            f.x = (max.x + (max.w / 2)) + 3
            f.w = (max.w / 2) - 9
        elseif direction == "up" then
            f.x = max.x + 6
            f.w = max.w - 12
        elseif direction == "down" then
            f.x = (max.x + (max.w / 8)) + 6
            f.w = (max.w * 3 / 4) - 12
        end
        f.y = max.y + 6
        f.h = max.h - 12
        win:setFrame(f, 0.0)
    end
end
hs.hotkey.bind(hyper, "Left", move_window("left"))
hs.hotkey.bind(hyper, "Right", move_window("right"))
hs.hotkey.bind(hyper, "Up", move_window("up"))
hs.hotkey.bind(hyper, "Down", move_window("down"))
hs.hotkey.bind(hyper, "H", move_window("left"))
hs.hotkey.bind(hyper, "L", move_window("right"))
hs.hotkey.bind(hyper, "K", move_window("up"))
hs.hotkey.bind(hyper, "J", move_window("down"))

使用 Hammerspoon 在切换 WiFi 时自动切换对应设置

当连接的 WiFi 发生变化的时候触发一个监听事件,更加详细的配置可以看我的 dotfiles

function ssidChangedCallback()
    newSSID = hs.wifi.currentNetwork()

    local devices = hs.usb.attachedDevices()

    if newSSID == homeSSID and lastSSID ~= homeSSID then
        -- We just joined our home WiFi network
        hs.alert.show("Welcome home!")
        hs.audiodevice.defaultOutputDevice():setVolume(25)
        -- result = hs.network.configuration:setLocation("Home")
        -- hs.alert.show(result)
    elseif newSSID ~= homeSSID and lastSSID == homeSSID then
        -- We just departed our home WiFi network
        hs.alert.show("left home!")
        hs.audiodevice.defaultOutputDevice():setVolume(0)
        -- result = hs.network.configuration:setLocation("Automatic")
        -- hs.alert.show(result)
    end

    if newSSID == workSSID then
        hs.alert.show("work karabiner setup")
        selectKarabinerProfile("goku")
    else
        hs.alert.show("built-in karabiner setup")
        selectKarabinerProfile("goku")
    end

    lastSSID = newSSID
end

wifiWatcher = hs.wifi.watcher.new(ssidChangedCallback)
wifiWatcher:start()

使用 Hammerspoon 一键布局桌面窗口

我抢了同事一个显示器使用,所以外接了三个显示器,在每一个显示器中都有默认的布局。我一般左边竖置的显示器常驻一个 Terminal,中间横置的一个显示器为主要工作的区域,一般放 IntelliJ IDEA,DataGrip,SmartGit 等等其他工具,右侧竖置的显示器上面为即时通信窗口,下面是浏览器。

使用 Hammerspoon 可以很快速的恢复所有窗口的布局,不过我自己用的并不多。

输入法自动切换

比如在特定应用中自动切换成 Rime 输入法 或者切换成 ABC 英文。比如在 IntelliJ IDEA 中不会输入中文的,直接切换成 ABC 输入英文即可,而当切换到浏览器的时候切换到 Rime。

结合 Rime 输入法的自动设置输入法的自动切换 就非常舒服了。

完美的代替了 kawa 这款切换输入法的工具。

定时自动执行脚本

比如我使用 Obsidian 来作笔记,同时使用 git 来做版本管理,写一个脚本,每 30 分钟提交一次。

log = hs.logger.new('autoscript', 'debug')
local cmdArr = {
    "cd /Users/einverne/Sync/wiki/ && /bin/bash auto-push.sh",
}

function shell(cmd)
    hs.alert.show("execute")
    log.i('execute')
    result = hs.osascript.applescript(string.format('do shell script "%s"', cmd))
    hs.execute(cmd)
end

function runAutoScripts()
    for key, cmd in ipairs(cmdArr) do
        shell(cmd)
    end
end

myTimer = hs.timer.doEvery(10, runAutoScripts)
myTimer:start()

比如定时提交 git commit,定时 git push 等等。当然直接使用 Crontab 来实现也是可以的。

Mute on sleep

在笔记本合上时静音

function muteOnWake(eventType)
  if (eventType == hs.caffeinate.watcher.systemDidWake) then
    local output = hs.audiodevice.defaultOutputDevice()
    output:setMuted(true)
  end
end
caffeinateWatcher = hs.caffeinate.watcher.new(muteOnWake)
caffeinateWatcher:start()

Locking the screen

定义锁屏的快捷键。

-- lock screen shortcut
hs.hotkey.bind({'ctrl', 'alt', 'cmd'}, 'L', function() hs.caffeinate.startScreensaver() end)

监听 USB 事件并做相应的设置

对我而言最常见的就是当我接入外接键盘的时候,自动切换 karabiner-Elements 的键盘 profile。

这样当我使用 macOS 自带的键盘和外置键盘的时候就可以保持一致的使用习惯。

绑定快捷键快速打开应用

快速打开终端:

hs.hotkey.bind({'ctrl', 'alt', 'cmd'}, 'K', function () hs.application.launchOrFocus("iTerm") end)

更多的例子可以参考我的 dotfiles

防止长时间不用进入休眠

参考 https://github.com/einverne/dotfiles/hammerspoon/ 关键字 Caffeine。

将当前窗口移动到其他屏幕中

定义了快捷键 Hyper + 1/2/3 将当前窗口快速移动到其他显示器:

function moveWindowToDisplay(d)
  return function()
    local displays = hs.screen.allScreens()
    local win = hs.window.focusedWindow()
    win:moveToScreen(displays[d], false, true)
  end
end

hs.hotkey.bind({"ctrl", "alt", "cmd"}, "1", moveWindowToDisplay(1))
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "2", moveWindowToDisplay(2))
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "3", moveWindowToDisplay(3))

对虚拟桌面的支持

非官方支持

扩展

Hammerspoon 的配置文件是使用 Lua 书写,如果熟悉 Lua,可以更进一步使用 Lua 的 moonscript 来简化配置。

brew install lua@5.3
luarocks-5.3 install moonscript
luarocks-5.3 install lodash

参考 这里

这里

Hammerspoon 官网文档

Spoon 是什么?

Spoon 是预置在 Hammerspoon 内的插件系统,Spoon 是使用纯 Lua 实现的插件,可以方便用户集成集成到 Hammerspoon 的配置中。

可以从官方的页面获取 Spoon ,源码可以参考对应的 GitHub 页面,下载后解压得到 .spoon 文件,双击导入即可。文件会自动将自己拷贝到 ~/.hammerspoon/Spoons/NAME.spoon,然后在 init.luahs.loadSpoon("NAME") 即可。

更具体的 Spoon 的使用可以参考官网。

编写 Spoon

Spoon 文件有一定的格式,方便集成调用。

Spoon 文件中的常用方法:

  • NAME: init(),这个方法会被 hs.loadSpoon() 调用,会进行一些初始设置,这里面不应该执行任何动作
  • NAME: start(),如果有需要在后台进行的任务,可以由这个方法启动
  • NAME: stop(),关闭后台任务
  • NAME: bindHotkeys(mapping),定义功能快捷键,通常是 table 的形式:

reference


2020-11-24 mac , hammerspoon , automation , tool , window-manage , mac-app , mac-application

电子书

本站提供服务

最近文章

  • AI Shell 让 AI 在命令行下提供 Shell 命令 AI Shell 是一款在命令行下的 AI 自动补全工具,当你想要实现一个功能,敲一大段命令又记不住的时候,使用自然语言让 AI 给你生成一个可执行的命令,然后确认之后执行。
  • 最棒的 Navidrome 音乐客户端 Sonixd(Feishin) Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。 Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。
  • 中心化加密货币交易所 Gate 注册以及认证 Gate.io 是一个中心化的加密货币交易所。Gate 中文通常被称为「芝麻开门」,Gate 创立于 2013 年,前身是比特儿,是一家致力于安全、稳定的数字货币交易所,支持超过 1600 种数字货币的交易,提供超过 2700 个交易对。
  • 不重启的情况下重新加载 rTorrent 配置文件 因为我在 Screen 下使用 rTorrent,最近经常调试修改 rtorrent.rc 配置文件,所以想要找一个方法可以在不重启 rTorrent 的情况重新加载配置文件,网上调查了一下之后发现原来挺简单的。
  • Go 语言编写的网络穿透工具 chisel chisel 是一个在 HTTP 协议上的 TCP/UDP 隧道,使用 Go 语言编写,10.9 K 星星。