Angular 最佳实践

  1. 控制器和Provider(Service,Factory)用于修改数据模型,而不是HTML
  2. HTML和指令定义布局,以及到模型的绑定
  3. 如果要在控制器之间共享数据,那么创建一个服务或工厂 - 它们是能在整个应用程序生命周期内共享的单件对象
  4. 如果你需要一个HTML Widget, 应该创建一个指令
  5. 如果你有一些数据,并且需要更新HTML,NO! NO! NO!, 更新模型就可以了,保证HTML绑定到模型就可以了.
TODO::后续有发现继续增加

在AngularJS中创建异步Alert Prompts Confirms

本文翻译自如下文章, 因为不是文学作品而是技术文章,因此不遵照信达雅的原则,让人容易理解即可, 没有逐句翻译原文的语言含义,而是根据其含义用更符合汉语的语言习惯来表达.

http://www.bennadel.com/blog/2632-creating-asynchronous-alerts-prompts-and-confirms-in-angularjs.htm

问题

有过在前端编程经验的人都知道,调用alert(), prompt(), 和confirm()会阻塞浏览器执行后续的Javascript代码,直到用户做出反应. 这个问题会让前端开发变得比较棘手.
比如,一个常见的问题是当alert()这类阻塞操作促发的时候,假设浏览器后台每隔N秒会向服务器请求更新数据,那么后台的Ajax更新也会被阻塞, 通常解决此类问题的方式转换到使用模态对话框.这种转换并不容易.因此促使原作者创建alert(),prompt()confirm()promise异步处理模式的非阻塞版本.

理解本文的关键是要理解promise异步处理模式, 关于什么是promise异步处理模式,这里不做解释,请自行Google/百度

本文的项目地址为: http://bennadel.github.io/JavaScript-Demos/demos/creating-asynchronous-prompts-angularjs/

如何打包和分发node-webkit桌面应用程序

准备额外的文件

下面的子目录可以放到应用程序的根目录:

  • node_modules 任何你想要同应用程序一起打包的Node.js模块都可以放在这里
  • plugins NPAPI插件文件

不需要打包 nwsnapshot 文件

警告:
do not assume your node_modules that target one platform work as is in all platforms. For instance node-email-templates has specific Windows & Mac os x npm install commands. Besides, it requires python to install properly, which is not installed by default on Windows.

As a rule of thumb npm install your package.json on each platform you target to ensure everything works as expected.

静态站点生成器

静态站点流行度排名
http://staticsitegenerators.net/

Static web site with Markdown (GitHub pages actually) / Jekyll alternatives
http://stackoverflow.com/questions/16504449/static-web-site-with-markdown-github-pages-actually-jekyll-alternatives

开源静态站生成器列表
http://www.staticgen.com/

Node.js

http://blog.bmannconsulting.com/node-static-site-generators

docpad
http://docpad.org

Hexo
https://github.com/tommy351/hexo

generator-pencil
http://www.html-js.com/topic/722

Heckle
https://github.com/marijnh/heckle
http://thechangelog.com/heckle-the-jekyll-clone-in-node-js/

Wintersmith
http://wintersmith.io/

Ruby

Jekyll
http://jekyllrb.com/

Octopress
http://octopress.org/

Nanoc
http://nanoc.ws/

PHP

node-webkit 剪切板

Clipboard API 需要 node-webkit >= v0.3.0 的支持

Clipboard 是一个剪切板(Windows,GTK)和粘贴板(Mac)的抽象,当前它仅支持在系统剪切板中读写纯文本.

Angularjs 预载指示器

ngActivityIndicator

简单Angular.js应用程序的预载动画provider

ngActivityIndicator 是一个小巧的,一体化方案,可定制皮肤,仅依赖 Angular.js

https://camo.githubusercontent.com/30660bc71d4b27d77c1c4c786ae50be35acc7129/68747470733a2f2f646c2e64726f70626f7875736572636f6e74656e742e636f6d2f752f3130303436333031312f6e674163746976697479496e64696361746f722d736d616c6c2e676966

安装

可以手动下载需要的ngActivityIndicator文件,或者通过bower前端包管理器来安装:

1
bower install ngActivityIndicator

Web性能测试

本文说明如何使用Google的PageSpeed Insights服务,node.js的PSI模块,以及自动build工具Grunt,Gulp把Web性能测试融入到项目构建过程中, 并生成性能分析报告.

安装

1
npm install --save psi

使用

1
var psi = require('psi');
psi({
    // key: '...', optional
    url: 'http://html5rocks.com',
    paths: '',           // optional
    locale: 'en_GB',     // optional
    strategy: 'mobile',  // optional
    threshold: 80        // optional
});

Erlang Win32Reg读写注册表信息

2012-07-29更新,

  1. 从文件中读取Erlang Term 格式的环境变量,并写入注册表
  2. win32reg现在只支持REG_SZ不支持REG_EXPAND_SZ,没有测试REG_SZ类型的数据,在环境变量中包含%VAR%形式的变量是否能够正常工作

读取保存在文件中的环境变量,并同步到注册表:

env.erl

1
%% Created: 2011-5-26
%% Description: TODO: Add description to env
%%
-module(env).
%% There are six entry points in the Windows registry,
%% top level keys. They can be abbreviated in the win32reg module as:
%% Abbrev.          Registry key
%% =======          ============
%% hkcr             HKEY_CLASSES_ROOT
%% current_user     HKEY_CURRENT_USER
%% hkcu             HKEY_CURRENT_USER
%% local_machine    HKEY_LOCAL_MACHINE
%% hklm             HKEY_LOCAL_MACHINE
%% users            HKEY_USERS
%% hku              HKEY_USERS
%% current_config   HKEY_CURRENT_CONFIG
%% hkcc             HKEY_CURRENT_CONFIG
%% dyn_data         HKEY_DYN_DATA
%% hkdd             HKEY_DYN_DATA
% 顶级路径必须小写
-define(CURRENT_USER, "\\hkey_current_user\\Environment").
-define(CURRENT_USER_TEST, "\\hkey_current_user\\EnvironmentTest").
%%
%% Exported Functions
%%
-export([environments/0]).
%%
%% Local Functions
%%
environments() ->
    % 以读写模式打开注册表
    {ok, Reg} = win32reg:open([write]),
    % 设置当前Key
    case win32reg:change_key(Reg, ?CURRENT_USER) of
        ok ->
            % Change key, 如果不存在就创建
            win32reg:change_key_create(Reg, ?CURRENT_USER_TEST),
            % 从文件中读取Tuples
            {ok, Envs} = file:consult("./win32reg/env.config"),
            % 逐条创建注册表Item
            Result = lists:foreach(fun({Key, Value}) ->
                win32reg:set_value(Reg, Key, Value) end, Envs),
            % 关闭注册表
            win32reg:close(Reg),
            % 返回值
            Result;
        _ ->
            false
    end.

保存在文件中的环境变量, 以Erlang Term的格式:

env.config

1
{"ANDROID","D:\\usr\\android-sdk-windows\\platform-tools"}.
{"ANT_HOME","D:\\usr\\apache-ant-1.8.0"}.
{"ARCHDIR","windows"}.
{"CAKE","D:\\chaw\\cakephp\\cake\\console"}.
{"CEAN_SERVER","http://cean.process-one.net"}.
{"ERLANG_HOME","D:\\usr\\erl5.9.1"}.
{"ffmpeg","D:\\usr\\ffmpeg-git-9d4cb45-32-bit-shared\\bin"}.
{"GETTEXT_TOOLS","D:\\usr\\gettext-tools-0.13.1.bin.woe32"}.
{"GIT","D:\\usr\\Git\\bin"}.
{"Haskell","D:\\usr\\Haskell\\2011.2.0.1"}.
{"HOME","C:\\Documents and Settings\\Administrator"}.
{"HTMLHELP","D:\\Program Files\\HTML Help Workshop"}.
{"JAVA_HOME","D:\\Program Files\\Java\\jdk1.6.0_24"}.
{"LUA_HOME","D:\\usr\\lua\\Lua\\5.1"}.
{"M2_HOME","D:\\usr\\apache-maven-3.0.3"}.
{"MAVEN_OPTS","-Xms256m -Xmx512m"}.
{"MongoDB","D:\\usr\\MongoDb\\mongodb-win32-i386-1.8.1\\bin"}.
{"MYSQL","D:\\usr\\mariadb-5.2.6-win32\\bin"}.
{"NODE_PATH","C:\\Documents and Settings\\Administrator\\Application Data\\npm\\node_modules;"}.
{"PANDOC","D:\\Program Files\\Pandoc\\bin"}.
{"PHP_HOME","D:\\usr\\php"}.
{"PROTO_BUFFER","D:\\usr\\protoc-2.4.1-win32"}.
{"PYJAMAS_HOME","G:\\pyjamas-0.8.1~+alpha1"}.
{"PYTHON_HOME","D:\\usr\\python26"}.
{"RabbitMQ","D:\\usr\\RabbitMQ Server\\rabbitmq_server-2.4.1\\sbin"}.
{"RABBITMQ_CONSOLE_LOG","new"}.
{"SVN","D:\\usr\\svn-win32-1.6.6\\bin"}.
{"TITANIUM_HOME","C:\\Documents and Settings\\Administrator\\Application Data\\Titanium\\mobilesdk\\win32\\1.7.2"}.

Windows的环境变量修改起来很坑,那个对话框不能最大化,看不到完整的东西.有时候变量路径又很长.

1
%% Created: 2011-5-26
%% Description: TODO: Add description to env
-module(env).
-define(CURRENT_USER, "\\hkey_current_user\\environment").
%%
%% Include files
%%
%%
%% Exported Functions
%%
-export([environments/0]).
%%
%% API Functions
%%
%%
%% Local Functions
%%
environments() ->
    % 打开注册表
    {ok, Reg} = win32reg:open([read]),
    % 设置当前Key
    case win32reg:change_key(Reg, ?CURRENT_USER) of
        ok ->
            % 读取当前Key下的所有值
            {ok, KVs} = win32reg:values(Reg),
            % 打开文件
            {ok, S} = file:open("data1.dat", write),
            lists:foreach(fun({KK,VV}) ->
                              io:format(S, "~p = ~p~n",[KK, VV]) % 写入
                       end, KVs),
            % 关闭注册表
            win32reg:close(Reg),
            % 关闭文件
            file:close(S);
        _ ->
            false
    end.

Varnish 负载均衡

Changlogs
2011-06-20 添加测试报告, 在另一台服务器上测试,1W并发,首页index.php无Cache,512M内存的VPS能达到这个程度也算不错.并发几千还是能承载的.就是没几个人来访问,哎^_*!

1
[root@localhost webbench-1.5]# ./webbench -c 10000 -t 30 http://www.hezhiqiang.info/index.php
Webbench - Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
Benchmarking: GET http://www.hezhiqiang.info/index.php
10000 clients, running 30 sec.
Speed=21030 pages/min, 3073918 bytes/sec.
Requests: 8441 susceed, 2074 failed.

Varnish 有内置的负载均衡支持. 如果有多个源服务器,使用起来非常简易.通过声明多个后端服务器启动Varnish,同时设置后端服务器的probe属性来监控后端服务器的健康状况.

1
/* 如果VCL定义文件内容太多可以分离到单独的文件,并包含进来 */
#include "backends.vcl";
#include "ban.vcl";
backend default {
	.host = "127.0.0.1";
	.port = "8080";
	/* Varnish 到后端服务器的连接超时, */
	.connect_timeout = 1s;
    /* 和后端服务器建立连接后,即进入等待接收响应状态,如果响应的第一个字节在5s内没有到达,被认为超时*/
	.first_byte_timeout = 5s;
    /* 第一个字节到达后, 如果后续字节在2s内没有到达,被认为是超时 */
	.between_bytes_timeout = 2s;
	.probe = {
		.url = "/";
		.interval = 5s;
		.timeout = 1 s;
		.window = 5;
		.threshold = 3;
    }
}
backend default8081 {
	.host = "127.0.0.1";
	.port = "8081";
	.connect_timeout = 1s;
	.first_byte_timeout = 5s;
	.between_bytes_timeout = 2s;
	.probe = {
		.url = "/";
		.interval = 5s;
		.timeout = 1 s;
		.window = 5;
		.threshold = 3;
    }
}

有了两个后端服务器定义, default 和 default8081 . probe对象指出,Varnish应该每隔5秒去获取一次 / 的内容.如果超过一秒还没有得到内容,被认为是失败.如果五次获取有三次成功,那么认为后端服务器状态被认为是健康. 健康状态检查的详细信息请查看 backend health polling

probe 在Varnish源码中定义为一个C Struct:

1
struct vrt_backend_probe {
	const char	*url;
	const char	*request;
	double		timeout;
	double		interval;
	unsigned	exp_status;
	unsigned	window;
	unsigned	threshold;
	unsigned	initial;
};

Director 是一个或多个后端服务器的逻辑组, 在启用健康检查的情况下,当有后端服务器不可用时, 它会自动切换到另一个后端服务器上去. 这在需要系统宕机维护, 同时又不能中断服务的情况下是非常有用的, 比如,凌晨2:00,系统维护,可以关闭一台后端服务器,维护完成后再启动,接着维护下一台,以此类推.

现在这两个后端需要被包含在一个逻辑的 director 中.被作为一个虚拟的后端,称之为 hezhiqiang:

1
directory hezhiqiang round-robin {
	{.backend = default;}
	{.backend = default8081;}
}

关键字 round-robin 指出,请求会以轮询的方式分布到后端服务器.当前(varnish 3.0)还支持另外的director, 包括 simple, hash, random, dns, client.

这些支持的后端负载均衡方式可以在源代码 cache_backend_cfg.c 文件中看到.

1
void
VRT_init_dir(struct cli *cli, struct director **dir, const char *name,
    int idx, const void *priv)
{
	ASSERT_CLI();
	if (!strcmp(name, "simple"))
		VRT_init_dir_simple(cli, dir, idx, priv);
	else if (!strcmp(name, "hash"))
		VRT_init_dir_hash(cli, dir, idx, priv);
	else if (!strcmp(name, "random"))
		VRT_init_dir_random(cli, dir, idx, priv);
	else if (!strcmp(name, "dns"))
		VRT_init_dir_dns(cli, dir, idx, priv);
	else if (!strcmp(name, "round-robin"))
		VRT_init_dir_round_robin(cli, dir, idx, priv);
	else if (!strcmp(name, "client"))
		VRT_init_dir_client(cli, dir, idx, priv);
	else
		INCOMPL();
}

现在可以把特定的请求定向到后端服务器了. 在 vcl_recv , 如下:

1
sub vcl_recv {
	# 把后端服务器指向上面定义的 directory
	set req.backend = hezhiqiang;
}

Client director

Client director 在 Vanish 2.1.3以及之后版本可用.

如果基于客户端提供的信息做负载均衡,比如IP地址, HTTP 头, 或则请求URL, 那么就可以使用client director

1
sub vcl_recv {
	/* Set which backend will be used  */
	set req.backend = hezhiqiang;
	/* Load balance by user agent */
	set client.identity = req.url;
	/* Load balance by user agent */
	#set client.identity = client.ip;
	/* Load balance by user agent */
	#set client.identity = req.http.user-agent;
}

DNS Director

指定384个后端服务器,其中全部在80端口监听, 0.4秒超时.

1
director directorname dns {
	.list = {
		.host_header = "www.example.com";
		.port = "80";
		.connect_timeout = 0.4;
		/* 使用掩码标识的网段作为后端 */
		"192.168.15.0"/24;
		"192.168.16.128"/25;
	}
	/* 指定DNS查询的缓存时间 */
	.ttl = 5m;
	/* 追加到客户端提供的主机头后面 */
	.suffix = "internal.example.net";
}

一个完整的配置

采用round-robin双Nginx后端.搭建在Linode的Plan512 VPS节点上,同时采用XCache作为PHP的加速器,Percona MySQL 服务器.

1
/*import std;*/
backend default {
	.host = "127.0.0.1";
	.port = "8080";
	/* Varnish 到后端服务器的连接超时, */
	.connect_timeout = 1s;
    /* 和后端服务器建立连接后,即进入等待接收响应状态,如果响应的第一个字节在5s内没有到达,被认为超时*/
	.first_byte_timeout = 5s;
    /* 第一个字节到达后, 如果后续字节在2s内没有到达,被认为是超时 */
	.between_bytes_timeout = 2s;
	.probe = {
		/* 健康检查的目标URL */
		.url = "/index.html";
		/* 每次检查间隔 */
		.interval = 5s;
		/* 超时 */
		.timeout = 1 s;
		/* 检查次数 */
		.window = 5;
		/* 成功次数 */
		.threshold = 3;
    }
}
backend default8081 {
	.host = "127.0.0.1";
	.port = "8081";
	.connect_timeout = 1s;
	.first_byte_timeout = 5s;
	.between_bytes_timeout = 2s;
	.probe = {
		.url = "/";
		.interval = 5s;
		.timeout = 1 s;
		.window = 5;
		.threshold = 3;
    }
}
director hezhiqiang round-robin {
	{.backend = default;}
	{.backend = default8081;}
}
sub vcl_recv {
	set req.backend = hezhiqiang;
	# Load balance by url
	set client.identity = req.url;
	# Load balance by client ip
	#set client.identity = client.ip;
	# Load balance by user agent
	#set client.identity = req.http.user-agent;
	# std.log("fishy is going on with the vhost" + req.http.host);
	if (req.request == "GET" && req.url ~ "\.(gif|jpg|png|swf|css|js)$") {
		unset req.http.Cookie;
	}
	# do not cache the /tag, /textpattern, and /category
	if ( req.url ~ "^/tag/" ) {
		return (pass);
	}
	if ( req.url ~ "^/textpattern/") {
		return (pass);
	}
	if ( req.url ~ "^/category/" ) {
		return (pass);
	}
	if ( req.url ~ "^/html/") {
		return (pass);
	}
}
sub vcl_deliver {
	# add debugging headers, so we can see what's cached
	set resp.http.X-Served-By = server.hostname;
	if (obj.hits > 0) {
		set resp.http.X-Cache = "HIT";
		set resp.http.X-Cache-Hits = obj.hits;
	}
	else {
		set resp.http.X-Cache = "MISS";
	}
	# remove some headers added by varnish
	# unset resp.http.Via;
	# unset resp.http.X-Varnish;
}

Erlang-分布式基础

2011-05-31 创建
2014-09-29 更新

知识点

  • 具有相同cookie而且彼此互相连接的节点集称为Erlang 集群
  • 设置Cookie的三种方式
    • $HOME/.erlang.cookie
    • erl -setcookie AFRTY12ESS3412735ASDF12378
    • erlang:set_cookie(node(),C)

Example

1
-module(kvs).
%% Include files
%% Exported Functions
-export([start/0, store/2, lookup/1]).
%% API Functions
start() ->
    io:format("starting server...~n"),
    % 启动kvs服务,派生一个新进程,进入循环
    register(kvs, spawn(fun()-> loop() end)).
store(Key, Value) ->
    io:format("store~n"),
    rpc({store, Key, Value}).
lookup(Key) ->
    io:format("call lookup~n"),
    rpc({lookup, Key}).
% Local Functions
% 消息代理函数
rpc(Q) ->
    io:format("rpc~n"),
    % 向进程kvs发送消息 {self(), Q},其中self()表示当前进程Pid, Q为参数
    kvs ! {self(), Q},
    receive
        {kvs, Reply} ->
            Reply
    end.
loop() ->
    io:format("entering loop...~n"),
    receive
        {From, {store, Key, Value}} ->
            io:format("value ~p with ~p stored~n", [Value, Key]),
            put(Key, {ok,Value}),
            From ! {kvs, true},
            loop();
        {From, {lookup, Key}} ->
            io:format("get the value of key: ~p~n", [Key]),
            From ! {kvs, get(Key)},
            loop()
    end

操作步骤

  • 启动两个终端, 分别输入
1
erl -sname server
erl -sname client
  • 在真实的分布式环境中,请使用
1
erl -name server
erl -name client

-name需要使用到DNS服务,而-sname不需要使用DNS,可用于本机测试,局域网也可以使用-sname启动Erlang系统.

启动服务器, 派生一个新进程,立即进入循环

1
(server@localhost) > kvs:start().
starting server...
entering loop...
true
  • 客户端通过RPC调用服务器函数 store 存储一个值
1
(client@localhost) > rpc:call('server@localhost',kvs,store,[google, "http://www.google.com"]).
  • 服务器输出,再次进入loop等待下一个消息:
1
(server@localhost) > value "http://www.google.com" with google stored
(server@localhost) > loop
  • 客户端查询
1
(client@localhost) > rpc:call('server@localhost',kvs,lookup,[google]).
call lookup
rpc
{ok,"http://www.google.com"}
  • 服务器输出
1
(server@localhost) > get the value of key: google
(server@localhost) > loop
  • 本地调用实际上也是通过rpc向kvs服务器发送消息
1
(server@localhost)2> kvs:lookup(google).
call lookup
rpc
get the value of key: google
loop
{ok,"http://www.google.com"}

消息传递图示

消息传递图示

结语

  • 本文使用进程字典作为Key,Value存储仅作为示例,真实环境中是不可能的.切忌照搬.
  • 可以把loop()函数中的put和get剥离到两个实现函数中
  • 采用什么后端存储就是你的事情了.

Erlang 正则表达式

1
Eshell V5.8  (abort with ^G)
% 最长单个匹配
regexp:match("linux,windows,erlang,php,mysql", "[a-z]+").
{match,7,7}
1
% 首个匹配
regexp:first_match("linux,erlang,mysql,mnesia", "[a-z]+").
{match,1,5}
1
% 匹配,并获取linux子串
1> {M,L} = regexp:matches("linux,erlang,mysql,mnesia", "[a-z]+"),
1> {Start, Length} = lists:nth(1, L),
1> Substring = string:substr("linux,erlang,mysql,mnesia",Start,Length),
1> io:format("The first substring is ~p~n", [Substring]).
The first substring is "linux"
ok
1
% 替换
regexp:sub("webmaster@gmail.com", "@[a-z\.]+", "@163.com").
{ok,"webmaster@163.com",1}
1
% 全局替换
regexp:gsub("webmaster@gmail.com,admin@gmail.com", "@[a-z\.]+", "@163.com").
{ok,"webmaster@163.com,admin@163.com",2}
1
% 切分
regexp:split("webmaster@gmail.com admin@gmail.com", "\s").
{ok,["webmaster@gmail.com","admin@gmail.com"]}
1
% 解析(编译)一次正则表达式,使之能够到处重用,类似Python的re.compile
7> RE = regexp:parse("[a-z]+").
{ok,{pclosure,{char_class,[{97,122}]}}}

Erlang 构建工具 rebar

更新历史:

  1. 2014-09-25 22:40:22

下载Rebar源码并编译
rebar命令
选项

https://bitbucket.org/basho/rebar/wiki/GettingStarted这篇文章是直接下载二进制的rabar,建议从源代码进行编译,可以了解rebar的更多信息.还有现成的rebar.config配置文件,下载编译过程很简单,请看图一

1
debian:~/erlang# hg clone https://bitbucket.org/basho/rebar
debian:~/erlang# cd rebar
debian:~/erlang# ./bootstrap

在src/myapp_app.erl文件的-export()指令后面添加如下行:

1
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

在文件的末尾添加如下行:

1
-ifdef(TEST).
simple_test() ->
    ok = application:start(myapp),
    ?assertNot(undefined == whereis(myapp_sup)).
-endif.
1
debian:~/erlang/rebar/myapp# ./rebat compile eunit

输出

1
debian:~/erlang/rebar/myapp# ./rebar compile eunit
==> myapp (compile)
==> myapp (eunit)
  Test passed.
Cover analysis: /var/root/erlang/rebar/myapp/.eunit/index.html

.eunit目录中包含了一些调试信息.

在rebar.config中添加如下行,让rebar为我们生成代码覆盖率信息.

1
{cover_enabled, true}.

执行:

1
debian:~/erlang/rebar/myapp# ./rebar clean
debian:~/erlang/rebar/myapp# ./rebar compile eunit
==> myapp (compile)
==> myapp (eunit)
  Test passed.
Cover analysis: /var/root/erlang/rebar/myapp/.eunit/index.html

覆盖率报告html文件生成了.

命令

Rebar提供了开发过程中大多数功能, 包括:

  • 编译
  • 单元测试和覆盖率分析
  • 静态分析 (Dialyzer and Xref).
  • 文档生成
  • 依赖管理

In addition, it allows for OTP embedded system generation, taking advantage of the template processing afforded by rebar and the reltool application.

The most common commands are:

COMMAND DESCRIPTION
compile Compile all the available source in the project.
eunit Perform unit testing using the Eunit application
doc Generate documention using the Edoc application
clean Remove any generated artifacts from compilation, unit testing, etc.

参考:

  1. http://www.cnblogs.com/musketeer/archive/2011/03/26/1996197.html
  2. http://www.linezing.com/blog/?p=347
  3. http://www.flatws.cn/article/program/embed/2011-05-05/24309.html

Erlang 使用Log4erl

更新历史:

  • 2014-09-25 23:34:31

功能

  • 支持多日志
  • 当前文件Appender仅支持基于大小的日志文件滚动
  • 支持默认Logger,未指定Logger时系统提供默认Logger
  • 5个预定义的日志级别(debug, info, warn, error, fatal)
  • 一个error_logger的日志处理器
  • 支持用户指定日志级别
  • 支持日志格式化
  • 支持控制台日志
  • 支持smtp formatter
  • 支持XML格式的日志
  • 支持syslog
  • 支持在运行时改变Appender的格式和级别.

Step1. Checkout from repository

你可以从 -Google code 或者- Github上获取Log4erl的源代码, Google code上的代码已经旧了,建议从Github上clone 代码

1
git clone git://github.com/ahmednawras/log4erl.git log4erl

Clone a copy from github
Checkout a copy from google code
Log4erl目录结构
编译Log4erl

Step2. 编译

如图4, 进入Log4erl源代码目录src执行:

1
make:all([{outdir, "../ebin"}]).

Step3. 安装

你要让Erlang能够找到Log4erl,两种方式

  1. 把整个Log4erl目录复制到$ERLANG_HOME/lib目录下面.看上面第三张图.
  2. 命令行指定
1
erl -pz /path/to/log4erl

Step4. 使用

1
application:start(log4erl).

创建配置文件并调用log4erl:conf(file)初始化

1
log4erl:conf("priv/log4erl.conf").

同样你可以用编程的方式对Log4erl进行配置

1
log4erl:add_logger(messages_log).
log4erl:add_console_appender(messages_log, cmd_logs, {warn, "[%L] %l%n"}).

好了,现在可以使用它了.

1
log4erl:info("Information message").

Log4erl配置文件格式

1
logger [<name>] {
       ...
}

<name> 指定了Logger的名称,你可以去任意你喜欢的名字.如果不指定任何名字,那么log4erl将把它作为默认的Logger使用,例如:

1
%% default logger
logger {
       ...
}

在一个Logger中,可以有一到多个Appender,例如

1
%% Default logger
%% it includes a file appender and a console appender
logger{
	file_appender app2{
		dir = ".",
		level = info,
		file = my_app,
		type = size,
		max = 100000,
		suffix = log,
		rotation = 5,
		format = '[%L] %I %l%n'
	}
	console_appender app1{
		level = warn,
		format = '%T %j [%L] %l%n'
	}
}

Appender的配置格式如下

1
<appender_type> <name> {
	...
}

在Appender中你可以用’property=value’的格式来设置Appender的属性,属性剑以’,’逗号分隔.每种Appender有不同的属性集合.

公共属性:

1
level   = <Level>     => 日志级别 (例如: warn)
format  = <F>		  => 输出格式 (查看 'Appenders.txt')

file_appender

1
dir         => 输出路径 (例如: /var/log/my_app)
file        => 日志文件名称 (例如: my_app_log)
type        => size,time. 当前仅实现了基于size的日志滚动
max         => 每次日志滚动的最大文件大小
suffix      => 日志文件后缀 (例如: log)
rotation    => 循环滚动次数,例如为5, 当滚动到第五个日志文件并且日志文件达到指定size的时候就会覆盖前面的日志文件,依次循环

smtp_appender

1
ip          => SMTP服务器IP地址
port        => SMTP服务器端口
no_auth
username    => 用户名
password    => 密码
from        => 寄信人地址
to          => 收信人地址
tilte       => 邮件标题
msg         => 邮件内容

syslog_appender

1
facility => Facility (例如: ftp)
host => 发送syslog消息的目标主机 [可选]
port => syslog 端口[可选]

本文源码:

https://github.com/developerworks/skypebot/tree/master/src/erlang/log4erl_example

参考资料:

  1. http://code.google.com/p/log4erl/wiki/Conf_Getting_Started
  2. http://code.google.com/p/log4erl/wiki/Log4erl_Manual_2