背景
acc
模块用来记录会话信息到syslog
,数据库
, AAA
和EVENTS
中,do_accounting()
函数并不是记录信息,只是标记会话信息,当会话完成时才去记录。
最小的记录信息包括:
- 请求
method
- From 头的
tag
- To 头的
tag
Call-ID
- 响应的
code
- 响应的
reason
- 会话完成的
时间戳
官方文档地址: acc,本次测试的opensips
版本是:
version: opensips 3.3.10 (x86_64/linux)
重要参数解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 记录响铃
modparam("acc", "early_media", 1)
# 上报CANCEL消息
modparam("acc", "report_cancels", 1)
# 控制请求方向
modparam("acc", "detect_direction", 1)
# 定义额外的参数
modparam("acc", "extra_fields", "log: a->My_a_Field; b")
# 定义多条腿的参数
modparam("acc", "leg_fields", "log: a->My_a_Field; b")
# 定义日志级别
modparam("acc", "log_level", 2)
# 定义数据表名
modparam("acc", "db_table_acc", "acc")
# 定义失败的数据表名
modparam("acc", "db_table_missed_calls", "missed_calls")
# 数据库地址
modparam("acc", "db_url", "DBURL")
|
重要函数解析
do_accounting(type, [flags], [table])
-
type: 类型,有以下几种类型:
log
: syslog
db
: database
aaa
: aaa
evi
: event
-
flags: 标志位,有以下几种标志位:
cdr
: 记录CDR
missed
: 记录未接电话
failed
: 当状态码>=300,记录失败信息
-
table: 数据表名
drop_accounting([type], [flags])
重置flag和types, 参数和do_accounting
函数一样。
- 不带参数时,所有的记录都会停止。
- 带一个参数时,只会停止指定的类型。
- 带两个参数时,只会停止指定的类型和标志位。
上报请求到日志,comment
必须是带有错误码的字符串,例如:480 NoBody Home
上报请求到数据库,comment
和acc_log_request
一样
上报请求到AAA,comment
和acc_log_request
一样
上报请求到EVENTS,comment
和acc_log_request
一样
acc_new_leg()
创建一个新腿,所有的参数为null.
acc_load_ctx_from_dlg()
加载和导出当前在用的dialog的accouting context. 必须和load_dialog_ctx()
一起使用
acc_unload_ctx_from_dlg()
卸载accouting context
EVENTS事件
E_ACC_CDR
cdr事件,参数:
method
from_tag
to_tag
callid
sip_code
sip_reason
time
evi_extra
evi_extra_bye
multi_leg_info
multi_leg_bye_info
duration
ms_duration
setuptime
created
E_ACC_EVENT
method
from_tag
to_tag
callid
sip_code
sip_reason
time
evi_extra
multi_leg_info
E_ACC_MISSED_EVENT
method
from_tag
to_tag
callid
sip_code
sip_reason
time
evi_extra
multi_leg_info
created
setuptime
实战
输出到syslog
使用官方配置:
配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
|
#
# OpenSIPS residential configuration script
# by OpenSIPS Solutions <team@opensips-solutions.com>
#
# This script was generated via "make menuconfig", from
# the "Residential" scenario.
# You can enable / disable more features / functionalities by
# re-generating the scenario with different options.#
#
# Please refer to the Core CookBook at:
# https://opensips.org/Resources/DocsCookbooks
# for a explanation of possible statements, functions and parameters.
#
####### Global Parameters #########
/* uncomment the following lines to enable debugging */
#debug_mode=yes
log_level=3
xlog_level=3
log_stderror=no
log_facility=LOG_LOCAL0
udp_workers=4
/* uncomment the next line to enable the auto temporary blacklisting of
not available destinations (default disabled) */
#disable_dns_blacklist=no
/* uncomment the next line to enable IPv6 lookup after IPv4 dns
lookup failures (default disabled) */
#dns_try_ipv6=yes
socket=udp:172.16.4.111:5261 # CUSTOMIZE ME
####### Modules Section ########
#set module path
mpath="/usr/local/lib/opensips/modules/"
#### SIGNALING module
loadmodule "signaling.so"
#### StateLess module
loadmodule "sl.so"
#### Transaction Module
loadmodule "tm.so"
modparam("tm", "fr_timeout", 5)
modparam("tm", "fr_inv_timeout", 30)
modparam("tm", "restart_fr_on_each_reply", 0)
modparam("tm", "onreply_avp_mode", 1)
#### Record Route Module
loadmodule "rr.so"
/* do not append from tag to the RR (no need for this script) */
modparam("rr", "append_fromtag", 0)
#### MAX ForWarD module
loadmodule "maxfwd.so"
#### SIP MSG OPerationS module
loadmodule "sipmsgops.so"
#### FIFO Management Interface
loadmodule "mi_fifo.so"
modparam("mi_fifo", "fifo_name", "/run/opensips/opensips_fifo")
modparam("mi_fifo", "fifo_mode", 0666)
#### USeR LOCation module
loadmodule "usrloc.so"
modparam("usrloc", "nat_bflag", "NAT")
modparam("usrloc", "working_mode_preset", "single-instance-no-db")
#### REGISTRAR module
loadmodule "registrar.so"
modparam("registrar", "tcp_persistent_flag", "TCP_PERSISTENT")
/* uncomment the next line not to allow more than 10 contacts per AOR */
#modparam("registrar", "max_contacts", 10)
#### ACCounting module
loadmodule "acc.so"
/* what special events should be accounted ? */
modparam("acc", "early_media", 1)
modparam("acc", "report_cancels", 1)
/* by default we do not adjust the direct of the sequential requests.
if you enable this parameter, be sure to enable "append_fromtag"
in "rr" module */
modparam("acc", "detect_direction", 0)
modparam("acc", "extra_fields", "log: a->$ci; b")
loadmodule "proto_udp.so"
####### Routing Logic ########
# main request routing logic
route{
if (!mf_process_maxfwd_header(10)) {
send_reply(483,"Too Many Hops");
exit;
}
if (has_totag()) {
# handle hop-by-hop ACK (no routing required)
if ( is_method("ACK") && t_check_trans() ) {
t_relay();
exit;
}
# sequential request within a dialog should
# take the path determined by record-routing
if ( !loose_route() ) {
# we do record-routing for all our traffic, so we should not
# receive any sequential requests without Route hdr.
send_reply(404,"Not here");
exit;
}
if (is_method("BYE")) {
# do accounting even if the transaction fails
do_accounting("log","failed");
}
# route it out to whatever destination was set by loose_route()
# in $du (destination URI).
route(relay);
exit;
}
# CANCEL processing
if (is_method("CANCEL")) {
if (t_check_trans())
t_relay();
exit;
}
# absorb retransmissions, but do not create transaction
t_check_trans();
if ( !(is_method("REGISTER") ) ) {
if (is_myself("$fd")) {
} else {
# if caller is not local, then called number must be local
if (!is_myself("$rd")) {
send_reply(403,"Relay Forbidden");
exit;
}
}
}
# preloaded route checking
if (loose_route()) {
xlog("L_ERR",
"Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
if (!is_method("ACK"))
send_reply(403,"Preload Route denied");
exit;
}
# record routing
if (!is_method("REGISTER|MESSAGE"))
record_route();
# account only INVITEs
if (is_method("INVITE")) {
do_accounting("log");
$acc_extra(a)=$ci;
}
if (!is_myself("$rd")) {
append_hf("P-hint: outbound\r\n");
route(relay);
}
# requests for my domain
if (is_method("PUBLISH|SUBSCRIBE")) {
send_reply(503, "Service Unavailable");
exit;
}
if (is_method("REGISTER")) {
# store the registration and generate a SIP reply
if (!save("location"))
xlog("failed to register AoR $tu\n");
exit;
}
if ($rU==NULL) {
# request with no Username in RURI
send_reply(484,"Address Incomplete");
exit;
}
# do lookup with method filtering
if (!lookup("location","m")) {
t_reply(404, "Not Found");
exit;
}
# when routing via usrloc, log the missed calls also
do_accounting("log","missed");
route(relay);
}
route[relay] {
# for INVITEs enable some additional helper routes
if (is_method("INVITE")) {
t_on_branch("per_branch_ops");
t_on_reply("handle_nat");
t_on_failure("missed_call");
}
if (!t_relay()) {
send_reply(500,"Internal Error");
}
exit;
}
branch_route[per_branch_ops] {
xlog("new branch at $ru\n");
}
onreply_route[handle_nat] {
xlog("incoming reply\n");
}
failure_route[missed_call] {
if (t_was_cancelled()) {
exit;
}
# uncomment the following lines if you want to block client
# redirect based on 3xx replies.
##if (t_check_status("3[0-9][0-9]")) {
##t_reply(404,"Not found");
## exit;
##}
}
|
配置文件解析:
modparam("acc", "extra_fields", "log: a->$ci; b")
, 这里只是定义了a
的key
为$ci
,
要使用$acc_extra(a)
来设置value
。通过这种方式,我们可以设置一些额外的参数输出到日志中。
输出的结果
- 正常接通挂断
sip信令图:

syslog输出:
1
2
3
4
5
6
7
|
May 13 10:07:20 [349] [130][23d4df0d6efe4cd481a6f4e29ca82f9b]---main--:INVITE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:07:22 [352] ACC: transaction answered: timestamp=1747102042;created=0;setuptime=1747102042;method=INVITE;from_tag=e2ff73efbd8242208c88592bda1d8d02;to_tag=06127b55;call_id=23d4df0d6efe4cd481a6f4e29ca82f9b;code=200;reason=OK;$ci=23d4df0d6efe4cd481a6f4e29ca82f9b;b=
May 13 10:07:22 [352] [130][23d4df0d6efe4cd481a6f4e29ca82f9b]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:07:22 [351] [130][23d4df0d6efe4cd481a6f4e29ca82f9b]---main--:INVITE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:07:22 [352] [130][23d4df0d6efe4cd481a6f4e29ca82f9b]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:07:24 [353] [130][23d4df0d6efe4cd481a6f4e29ca82f9b]---main--:BYE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:07:24 [351] ACC: transaction answered: timestamp=1747102044;created=0;setuptime=1747102044;method=BYE;from_tag=e2ff73efbd8242208c88592bda1d8d02;to_tag=06127b55;call_id=23d4df0d6efe4cd481a6f4e29ca82f9b;code=200;reason=OK;$ci=;b=
|
使用官方的配置,可以看到只有两个记录了INVITE
和BYE
,没有响铃
。另外,在BYE
时,$acc_extra(a)
的值为空。
现在修改INVITE
处的do_accounting("log");
为do_accounting("log", "cdr|missed|failed");
,再次测试,syslog输出如下:
1
2
3
4
5
6
7
|
May 13 10:36:06 [535] NOTICE:signaling:mod_init: initializing module ...
May 13 10:36:14 [550] [132][ec40d522330f4171866f26f9bd1a1499]---main--:INVITE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:36:15 [550] [132][ec40d522330f4171866f26f9bd1a1499]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:36:15 [550] [132][ec40d522330f4171866f26f9bd1a1499]---main--:INVITE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:36:15 [550] [132][ec40d522330f4171866f26f9bd1a1499]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:36:17 [550] [132][ec40d522330f4171866f26f9bd1a1499]---main--:BYE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:36:17 [553] ACC: call ended: created=1747103774;call_start_time=1747103775;duration=3;ms_duration=2455;setuptime=1;method=INVITE;from_tag=82c34d97660b4e6fb6c1d8ccf04c194d;to_tag=7f0a2548;call_id=ec40d522330f4171866f26f9bd1a1499;code=200;reason=OK;$ci=ec40d522330f4171866f26f9bd1a1499;b=
|
只有一个记录,但是带了其他信息:call_start_time
:电话接通的时间戳、duration
:通话时长s、ms_duration
:通话时长ms,setuptime
:电话建立的时间,可以认为是响铃时长s,created
:此记录开始的时间.
- 未接电话
sip信令图:

syslog输出:
1
2
3
4
|
May 13 10:50:51 [622] [132][67abdb49d25f4d579670b78b56d4f0a3]---main--:INVITE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:50:52 [622] ACC: call missed: timestamp=1747104652;created=1747104651;setuptime=1;method=INVITE;from_tag=2cf172b645e04628a1ace902e877512b;to_tag=7f10ee66;call_id=67abdb49d25f4d579670b78b56d4f0a3;code=480;reason=Temporarily Unavailable;$ci=67abdb49d25f4d579670b78b56d4f0a3;b=
May 13 10:50:52 [622] [312][67abdb49d25f4d579670b78b56d4f0a3]---missed_call----:INVITE|<null>|sip:1009@172.16.4.111|5261
May 13 10:50:52 [622] [132][67abdb49d25f4d579670b78b56d4f0a3]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
|
可以看到记录了missed_call
- 主叫CANCEL
sip信令图:

syslog输出:
1
2
3
4
|
May 13 10:57:39 [685] [132][7b53a5cf8c9741db9eccc429e719a7b1]---main--:CANCEL|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 10:57:39 [686] ACC: call missed: timestamp=1747105059;created=1747105057;setuptime=2;method=INVITE;from_tag=52d3da242e4f47e0ab21cbb95afc715c;to_tag=8f3e627c;call_id=7b53a5cf8c9741db9eccc429e719a7b1;code=487;reason=Request Terminated;$ci=7b53a5cf8c9741db9eccc429e719a7b1;b=
May 13 10:57:39 [686] [312][7b53a5cf8c9741db9eccc429e719a7b1]---missed_call----:INVITE|<null>|sip:1009@172.16.4.111|5261
May 13 10:57:39 [685] [132][7b53a5cf8c9741db9eccc429e719a7b1]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
|
可以看到记录了missed_call
, 但是应该是487
信令的,而不是CANCEL
.
输出到数据库
配置文件
在syslog输出的基础上,修改配置文件:
1
2
|
modparam("acc", "extra_fields", "log: a->$ci; b")
modparam("acc", "db_url", "DBURL")
|
把所有的do_accounting("log","xxx")
改成do_accounting("log|db","xxx")
,再次测试。
输出的结果
- 正常接通挂断
数据表记录:

相对于kamailio
,opensips
的数据库记录更加详细,把CDR
的信息一起记录到了acc
表。
- 未接电话
数据表记录:

数据记录在missed_call
表
- 主叫CANCEL
数据表记录:

数据记录在missed_call
表
输出到EVENTS
配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
loadmodule "event_route.so"
...
event_route[E_ACC_CDR]{
xlog("L_DBG","[$cfg_line]---event_route[E_ACC_CDR]----:$param(method)|$param(from_tag)|$param(to_tag)|$param(callid)|$param(sip_code)|$param(sip_reason)|$param(time)|$param(duration)|$param(ms_duration)|$param(created)|$param(setuptime)\n");
}
event_route[E_ACC_EVENT]{
xlog("L_DBG","[$cfg_line]---event_route[E_ACC_EVENT]----:$param(method)|$param(from_tag)|$param(to_tag)|$param(callid)|$param(sip_code)|$param(sip_reason)|$param(time)\n");
}
event_route[E_ACC_MISSED_EVENT]{
xlog("L_DBG","[$cfg_line]---event_route[E_ACC_MISSED_EVENT]----:$param(method)|$param(from_tag)|$param(to_tag)|$param(callid)|$param(sip_code)|$param(sip_reason)|$param(time)|$param(created)|$param(setuptime)\n");
}
|
把do_accounting("log|db","xxxx");
全部替换为do_accounting("log|db|evi","xxxx");
输出结果
- 正常接通挂断
event_route输出:
1
2
3
4
5
6
7
|
May 13 11:42:32 [959] [137][8b8ae3d6728a4b04bf6875f005730039]---main--:INVITE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 11:42:33 [959] [137][8b8ae3d6728a4b04bf6875f005730039]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 11:42:33 [961] [137][8b8ae3d6728a4b04bf6875f005730039]---main--:INVITE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 11:42:34 [957] [137][8b8ae3d6728a4b04bf6875f005730039]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 11:42:36 [959] [137][8b8ae3d6728a4b04bf6875f005730039]---main--:BYE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 11:42:36 [959] ACC: call ended: created=1747107752;call_start_time=1747107753;duration=3;ms_duration=2446;setuptime=1;method=INVITE;from_tag=5af280175d2f4bb8b32d883e3286cc4a;to_tag=897c787b;call_id=8b8ae3d6728a4b04bf6875f005730039;code=200;reason=OK;$ci=8b8ae3d6728a4b04bf6875f005730039;b=
May 13 11:42:36 [957] [333]---event_route[E_ACC_CDR]----:INVITE|5af280175d2f4bb8b32d883e3286cc4a|897c787b|8b8ae3d6728a4b04bf6875f005730039|200|OK|1747107753|3|2446|1747107752|1
|
可以看到在E_ACC_CDR
的信息和syslog
的一致.
- 未接电话
event_route输出:
1
2
3
4
5
|
May 13 11:45:40 [959] [137][cfb5b5aa7a4f4ed081ced6764ae58867]---main--:INVITE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 11:45:43 [959] [343]---event_route[E_ACC_MISSED_EVENT]----:INVITE|67c43acbbb68485e8437d660ab210df3|ba1cbf25|cfb5b5aa7a4f4ed081ced6764ae58867|480|Temporarily Unavailable|1747107943|1747107940|3
May 13 11:45:43 [958] ACC: call missed: timestamp=1747107943;created=1747107940;setuptime=3;method=INVITE;from_tag=67c43acbbb68485e8437d660ab210df3;to_tag=ba1cbf25;call_id=cfb5b5aa7a4f4ed081ced6764ae58867;code=480;reason=Temporarily Unavailable;$ci=cfb5b5aa7a4f4ed081ced6764ae58867;b=
May 13 11:45:43 [958] [317][cfb5b5aa7a4f4ed081ced6764ae58867]---missed_call----:INVITE|<null>|sip:1009@172.16.4.111|5261
May 13 11:45:43 [957] [137][cfb5b5aa7a4f4ed081ced6764ae58867]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
|
可以看到数据输出到E_ACC_MISSED_EVENT
, 在E_ACC_MISSED_EVENT
的信息和syslog
的一致.
- 主叫CANCEL
event_route输出:
1
2
3
4
5
6
|
May 13 11:46:47 [958] [137][df003e31dace43b298e30ef175d99343]---main--:INVITE|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 11:46:48 [957] [137][df003e31dace43b298e30ef175d99343]---main--:CANCEL|<null>|sip:1009@172.16.4.111|5261|<null>
May 13 11:46:49 [957] [343]---event_route[E_ACC_MISSED_EVENT]----:INVITE|bd65219bbfb74f5a859fca3126483d6e|e11fff0b|df003e31dace43b298e30ef175d99343|487|Request Terminated|1747108009|1747108007|2
May 13 11:46:49 [960] ACC: call missed: timestamp=1747108009;created=1747108007;setuptime=2;method=INVITE;from_tag=bd65219bbfb74f5a859fca3126483d6e;to_tag=e11fff0b;call_id=df003e31dace43b298e30ef175d99343;code=487;reason=Request Terminated;$ci=df003e31dace43b298e30ef175d99343;b=
May 13 11:46:49 [960] [317][df003e31dace43b298e30ef175d99343]---missed_call----:INVITE|<null>|sip:1009@172.16.4.111|5261
May 13 11:46:49 [957] [137][df003e31dace43b298e30ef175d99343]---main--:ACK|<null>|sip:1009@172.16.4.111|5261|<null>
|
可以看到数据输出到E_ACC_MISSED_EVENT
, 在E_ACC_MISSED_EVENT
的信息和syslog
的一致.
总结
opensips
的extra_fields
设置没有kamailio
的方便。
180响铃
,opensips
和kamailio
一样,都未上报,183
可能会上报。
- 使用数据库时,
opensips
只用到两个表,kamailio
用到了三个表。
opensips
的上报方式更丰富些.