QueueMetrics is designed to analyze queue_log data provided by any Asterisk installation; the following guidelines will help you to make the most out of it.
In the following example:
Extensions.conf
[q-my-sample]
; ...queue description.....
exten => s,1,SetVar(MONITOR_FILENAME=/var/spool/asterisk/QSAMPLE-${UNIQUEID})
exten => s,2,Queue(q-sample|nt|||60)
exten => s,3,Playback(voicemail-invitation)
exten => s,4,VoiceMail,s2001Queues.conf
[q-sample] music = default announce = q-sample-announce strategy = roundrobin timeout = 60 retry = 5 maxlen = 0 announce-frequency = 0 announce-holdtime = no monitor-format = wav monitor-join = yes queue-youarenext = silence queue-thankyou = q-sample-thankyou member=>Agent/302,0 member=>Agent/303,0 member=>Agent/301,1
Make sure that you do not forget the explicit timeout when calling the Queue() command from extensions.conf, or queue timeouts will not be logged by Asterisk and therefore not reported by QM. A patch that corrects this Asterisk behaviour can be found at http://bugs.digium.com/view.php?id=5422 .
The URL should be embedded in the Queue() command as prescribed by Asterisk:
exten => s,7, Queue(myqueue|nt|http://mysite/app?uid=${UNIQUEID}&clid=${CALLERID}||60)This command launches the queue "myqueue" and launches the webapp located http://site/app passing the following parametrs:
The URL will appear on a clickable link on the Agent’s page.
If you set the property realtime.agent_autoopenurl to true, whenever the Agent’s page is reloaded, the most recent unopened URL is launched automatically.
Tell Asterisk to record all calls To record all calls add something like this to extensions.conf:
exten => s,1,SetVar(MONITOR_FILENAME=/var/spool/asterisk
/q/QSAMPLE-${UNIQUEID})
exten => s,2,Queue(q-sample|nt|||60)This way all sound files are stored under /var/spool/asterisk/q/ with the name of the queue (QSAMPLE) followed by the call id.
Tell QueueMetrics where to look for the calls You should set up the WEB-INF/configuration.property file in QM like this:
default.monitored_calls=/var/spool/asterisk/q/
When looking for the recording of a call, QM will explore all files contained in /var/spool/asterisk/q/ and any directories below for a file name containing the right call ID. It might find more than one file name and will display all of them. It is possible that sometimes Asterisk fails at mixing together the two files (Asterisk records separate files for the caller and the agent, and then tries to mix them together at the end of the call) so you will find two files named -in and -out instead. The search behaviour can be customized -see Section 19, “Listening to calls using Pluggable Modules (PM)”.
AddQueueMember is a command that lets you add dynamic agents to a queue. Its main advantage is that you can add channels, i.e. terminals, so you’ll have most of the advantages of agents without the performance and stability problems that the agents module may cost in very large systems.
Its disadvantage is that it does not log the agent login/logoff to the queue_log, and so programs that analyze the queue log data like QueueMetrics will not see agents logging on and off. This is a major organizational problem in a real-world call center, where tracking agent logons and logoffs is vital to the smooth running of the operations.
The answer is to add a fake queue_log data for each logon and logoff. For QM, it is important to avoid multiple logoff lines and to compute online permanence with logoffs.
To do the adding, you dial 422XX, where XX is your local extension; the same happens with 423XX to be logged off.
; Add Member - 422
exten => _422XX,1,Answer
exten => _422XX,2,AddQueueMember(my-queue,SIP/${EXTEN:3})
exten => _422XX,3,System( echo "${EPOCH}|${UNIQUEID}|NONE|SIP/${EXTEN:3} \
|AGENTLOGIN|-" >> /var/log/asterisk/queue_log )
exten => _422XX,4,DBput(dynlogin/log_Agent-${EXTEN:3}=${EPOCH})
exten => _422XX,5,Hangup
; Remove Member - 423
exten => _423XX,1,Answer
exten => _423XX,2,RemoveQueueMember(my-queue,SIP/${EXTEN:3})
exten => _423XX,3,DBget(ORGEPOCH=dynlogin/log_Agent-${EXTEN:3})
exten => _423XX,4,Set(RV=$[${EPOCH} - ${ORGEPOCH}])
exten => _423XX,5,GotoIf($["${RV}" = "0"]?8:6)
exten => _423XX,6,System( echo "${EPOCH}|${UNIQUEID}|NONE|SIP/${EXTEN:3}|\
AGENTLOGOFF|-|${RV}" >> /var/log/asterisk/queue_log )
exten => _423XX,7,DBdel(dynlogin/log_Agent-${EXTEN:3})
exten => _423XX,8,HangupWith this setup, we verified that the queue_log can be analyzed by QueueMetrics and the dynamic agent shows up fine (albeit with the name of a terminal, like SIP/23, instead of the usual Agent/23 string, but you can modify it in QM itself).
This setup might even be used in a call center where agents are not actually used but queues connect straight to terminals to "fake" agent logon/logoff, in order to have such data available for reporting.
Standard Asterisk queues are, by definition, inbound queues; they accept a number of incoming calls, let them wait in line and distribute them to various agents based on the queue logic.
To make it possible to analyze outbound calls with QM, we added the concept of a "campaign" or "outbound queue", that is a set of calls made by different agents that are working for the same purpouse. Of course there is no such thing as an outbound queue in Asterisk, so we have to run a special piece of dialplan or an AGI script to produce the same information on queue_log for outbound calls as it is automatically produced for inbound queues.
As this only regards the actual Dial(…) statement that Asterisk runs, it is possible to have different sources of numbers to be dialled by agents on outbound queues; they might enter the number on their keypad, or use the telephone, launch them from the Agent’s page or maybe use a predictive dialler for the task. QueueMetrics does not care, as long as the correct events are logged.
If you run Asterisk 1.4 or newer and want to place outbound calls, you use an example script supplied within the extensions_queuemetrics.conf; it should be imported by the main Asterisk configuration.
After this, if you place a call directed to Local/XXXYYYYYYY@queuedial, where XXX is the code for the campaign and YYYYYY…. the number to be dialled, a call will be created and logged as Agent/ZZZ, where ZZZ is the caller-id of the extension placing the call.
You may want to tweak the following supplied piece of dialplan to adapt it to your needs:
[queuedial]
exten => _XXX.,1,Set(QDIALER_QUEUE=q-${EXTEN:0:3})
exten => _XXX.,n,Set(QDIALER_NUMBER=${EXTEN:3})
exten => _XXX.,n,Set(QDIALER_AGENT=Agent/${CALLERID(num)})
exten => _XXX.,n,Set(QDIALER_CHANNEL=SIP/${QDIALER_NUMBER})
exten => _XXX.,n,Set(QueueName=${QDIALER_QUEUE})
exten => _XXX.,n,MixMonitor(Q-${QDIALER_QUEUE}-${UNIQUEID}.WAV|b|)
exten => _XXX.,n,Goto(qm-queuedial,s,1)You can/should modify the following variable definitions:
Please note that:
This section applies only if you run a version of Asterisk 1.0 or 1.2; for 1.4 or newer, please use the dialplan logic supplied in the file extensions_queuemetrics.conf.
The AGI script to be used instead of the Dial(…) command is available in the standard QM distribution and can be used in the following way:
exten => xxx,1,DeadAGI(queueDial.agi|Number|DialString|QueueName|Agent)
The following parameter have to be passed by dialplan logic:
A working example might be the following:
exten => 426,1,DeadAGI(queueDial.agi|34|SIP/34|queue-out-1|Agent/101)
The terminal SIP/34 is dialled and the resulting events are logged as if generated by Agent/101 working on queue-out-1.
Please note:
To get the AGENTATTEMPT code to work, it is necessary to patch the Asterisk module called app_queue.c in order to track down the required information. In order to perform this task, you must be confident with general Unix project patching and recompiling. It is advisable that Asterisk be shut down before applying the patch.
In order to apply the patch, just copy the file app_queue_agentattempt.patch found under WEB-INF/README/ to the apps/ directory of your Asterisk project, and then issue the following statement:
patch -p0 < app_queue_agentattempt.patch
As long as you see no errors, the patching process worked successfully. It’s now time to rebuild the app by issuing a general make statement from the main Asterisk directory.
Restart Asterisk and check that the queue system is still working fine.
To see if the patch was correct, try dialling a queue and see that Asterisk writes AGENTATTEMPT records to the queue_log file.
Asterisk 1.4 is natively able to produce the RINGNOANSWER log entry that servers the same purpose of AGENTATTEMPT, so no patching is necessary.
In order to implement this feature, QueueMetrics follows the following steps:
To enable unattended audio monitoring for inbound calls, you’ll have to edit the Asterisk dial-plan in order to include the [queuemetrics] context.
To enable unattended call monitoring for outgoing calls as well, you’ll have to set the piece of dial-plan referenced by the callfile.outmonitoring… properties.
Outgoing calls placed though queueDial.agi will usually be listened to by attaching to the local SIP/XXX or Local/XXX channel of the calling agent and not to the standard Agent/XXX channel used for inbound, so a different piece of dial-plan will be used. Note that in order for QueueMetrics to reference the outgoing calls, you must tell it that queue direction is Outgoing.
See also Appendix C, The [queuemetrics] context for an example of implementing Asterisk code for inbound and outbound call monitoring.
It is possible to use different PMs to handle different live audio - see Section 19, “Listening to calls using Pluggable Modules (PM)”.
To enable VNC monitoring you will first need a VNC server that is running on each client’s machine and that will serve the current layout.
You will also have to create a web page with a VNC client that may accept a VNC URL and show a VNC client (there are a number of Java-based VNC clients that can be displayed as an applet).
Configure the VNC URL as something like: http://myserver/vncpage.php?ip=192.168.3.17
Where the PHP page will connect the VNC applet to the server located on address 192.168.3.17
Make sure that your users hold the MON_VNC key in order to be able to access this feature.
As an alternative, we have some clients that use a simpler setup with each machine having their own copy of UltraVNC - http://ultravnc.sourceforge.net/ - and each machine running a web server with the locally-configured Java viewer. The VNC url is then the address of the local machine; when a person connects to it, s/he is asked for a password and then the screen is displayed through a Java applet. They report this setup to be very simple and working very well.
In order to enable actions on the Agent’s page:
It is possible to run remote audio monitoring of both completed and ongoing calls using third party monitoring tools, for example Orecx. As QueueMetrics has no way of knowing the internal details of such applications, we made it possible to call an external XML-RPC server (we offer a stub written in PHP, but it can be written in any language and reside on any server, as long as it uses an XML-RPC library) that will basically pass back to QM the URLs required to perform the required task.
In order to enable this, we first tell QueueMetrics to use the XML-RPC Pluggable Modules for both call listening and streaming:
audio.server=it.loway.app.queuemetrics.callListen.listeners.ClassicXmlRpcRecordings audio.liveserver=it.loway.app.queuemetrics.callListen.RTlisteners.ClassicXmlRpcListenerRT
The XML-RPC server will be set by setting its URL in a configuration property, like for example:
default.audioRpcServer=http://127.0.0.1/xmlrpc/xmlrpc_audio_server.php
The server must implements three XML-RPC calls called:
QMAudio.findStoredFile This function is used to find and play back a stored audio file, by returning the URL of a player that will play it or the audio file itself. This function has in input the following parameters:
and it must return the following values:
QMAudio.listenOngoingCall This function is used to query for an ongoing inbound call. If found, QM will launch a new popup to open the player which URL is returned. This function has in input the following parameters:
and it must return the following values:
To make implementer’s life easier, we provide a simple XML-RPC stub server under WEB-INF/mysql-utils/xml-rpc that can be used as a starting point: no need to handle the XML-RPC stuff, just change the results of the two supplied functions and data goes back to QueueMetrics.
A call tracking code is a code to be input by a user telling the status of a call, be it inbound or outbound. This status code is a string (though we suggest to use numeric status codes, in order to make it easy to input them using a telephone keypad) and may be input either when the call is ongoing or after a short while from its end.
The queue_log entry looks like the following one:
1234|1231.1|NONE|Agent/1234|CALLSTATUS|21
This will set the CALLSTATUS to "21" for the call which Call-ID is "1231.1" it may be an open call or it may be terminated by no longer than 30 minutes.
If it is not possible to force the Call-ID, a second version of the verb is available:
1234|2222.3|NONE|Agent/1234|CALLSTATUS|21|1231.1
This has exactly the same meaning; the second Call-ID passed as a parameter will override the original one.
If you prefer, you may log the queue name instead of "NONE" field shown above; in any case QM will ignore this piece of information.
The following rules apply:
The agent may either be a fill "Agent/xxx" string or the valid name of an Asterisk channel. It is acceptable to use a generic channel name instead of the specific one, i.e. "SIP/123" and "SIP/123-abcd" are equivalent.
The sample [queuemetrics] context that comes with QueueMetrics can be used as a starting point to output such data.
A pause reason code is a code to be input by a user telling the reason why a pause was started. It should be ideally input together with the decision to go on pause, though QueueMetrics will accept the code and will attach to the correct pause even if the pause is resumed, as long as no other pause is started. The reason code is a string - though we suggest to use numeric status codes, in order to make it easy to input it using a standard telephone keypad.
The format is the following one:
1234|1231.1|NONE|Agent/1234|PAUSEREASON|21
This will set the pause reason to "21" for the pause that is either going on or has just finished. If the code is input after over 30 minutes from the end of the last pause, it is discarded.
The following rules apply:
The sample [queuemetrics] context that comes with QueueMetrics can be used as a starting point to output such data.
It sometimes happens that Asterisk will not log the call termination records for a call; as QM is based on the logged events, a call missing the call closure log will linger on forever in the realtime screen (or at least the maximum time allowed by the …) and will appear as Ongoing or Not answered yet in the historical reports.
Since version 1.4.5 of QueueMetrics, it is possible to manually close a call from either the historical reports or the real-time screen. In order for this to work:
When this is done, open calls on the reports will show a red scissor icon:

And the same will happen for the real-time screen:

By clicking on that icon, a popup will appear that will ask for the length the call should be closed to. This length refers to the wait duration if the call is not answered and the conversation time if the call is answered. It is possible to change that from the default 5 seconds by setting a configuration property.
If the call has already been closed in the meantime, or you’re doing this operation twice, QM will report that the call has already been closed.
| Important | |
|---|---|
if you do this on calls that are still ongoing, you will risk having duplicate data on the report. So don’t use this feature unless you know what you are doing. The required security key must be manually assigned only to trusted users. |
In order to keep track of DNIS and IVR information that relates to each call, you have to write special records on the ’queue_log’ file that QueueMetrics parses.
This is very easy to do, e.g. imagine you have a piece of dialplan where you are going to call queue ’q-sample’ and you have the DNIS code in the ’MYDNIS’ dialplan variable, and the sequence of keys pressed as ’MYIVR’:
exten => s,n,........
exten => s,n,QueueLog(q-sample,${UNIQUEID},NONE,INFO,DID|${MYDNIS})
exten => s,n,QueueLog(q-sample,${UNIQUEID},NONE,INFO,IVR|${MYIVR})
exten => s,n,Queue(q-sample|nt|||60)
exten => s,n,........There is no predefined format for DNIS and IVR information; QueueMetrics just handles it as free-form text strings.
You can output only one record, or both, or none, depending on what you need.
Since the demise of AgentCallBackLogin, it has been hard to do "hotdesking" in Asterisk - that is, having agents that work on queues because of their competences and not because they are sitting at a given extension.
With QueueMetrics 1.6.1, hotdesking is very easy to implement and it has no downsides, because:
Requirements:
Set the following properties as follows:
default.queue_log_file=sql:P001 <-- change as needed callfile.dir=tcp:admin:amp111@127.0.0.1 <-- change as needed default.rewriteLocalChannels=true callfile.agentlogin.enabled=false callfile.agentlogoff.enabled=false default.hotdesking=86400
Make sure that ’extensions_queuemetrics.conf’ is loaded in the Asterisk dialplan (you need to use the extensions_queuemetrics file that comes with QM 1.6.1 or newer).
This setup means that we access the queue_log file through the database, connect to Asterisk over AMI to send commands, rewrite agent codes, do not use Agentcallback-style agents and enable hotdesking.
Now we use a piece of dialplan like this one when we associate an agent to a queue:
Imagine we have AGENTCODE set to 200 (the agent’s login code) and AGENT_EXT set to 123 (thi sis the SIP extension code):
....
exten => 35,3,QueueLog(NONE,${UNIQUEID},Agent/${AGENTCODE},HOTDESK,SIP/${AGENT_EXT})
exten => 35,4,AddQueueMember(myqueue,SIP/${AGENT_EXT})
....This logs on Agent/200 to queue "myqueue", tracking him as SIP/123. Note that from the point of view of Asterisk, we only see that extension 123 is made a member of the queue.
When you logoff, pause, unpause agents, you always work at the SIP level (the actual extension that is linked to the queue) so there is no need to change anything.
If you use the QueueMetrics Agent’s page, you can do logon/logoffs/pauses from the buttons by the top of the page; this lets you add an agent to all queues at once, like you used to do with AgentCallBackLogins, and still retain the flexibility to change that at runtime.
In the following sections, we sumamrize the changes that have to be made to an existing system to enable hotdesking.
Add/change the ’default.hotdesking’ property to 86400. This property enables hotdesking and lets the parse "look back" up to 1 day (change as needed).
default.hotdesking=86400
Add/change the sections below:
callfile.agentpause_ht.enabled=true callfile.agentpause_ht.channel=Local/32@queuemetrics/n callfile.agentpause_ht.extension=10 callfile.agentpause_ht.context=queuemetrics callfile.agentunpause_ht.enabled=true callfile.agentunpause_ht.channel=Local/33@queuemetrics/n callfile.agentunpause_ht.extension=10 callfile.agentunpause_ht.context=queuemetrics callfile.agentaddmember_ht.enabled=true callfile.agentaddmember_ht.channel=Local/35@queuemetrics/n callfile.agentaddmember_ht.extension=10 callfile.agentaddmember_ht.context=queuemetrics callfile.agentremovemember_ht.enabled=true callfile.agentremovemember_ht.channel=Local/37@queuemetrics/n callfile.agentremovemember_ht.extension=10 callfile.agentremovemember_ht.context=queuemetrics
This code specifies the Asterisk extensions that QueueMetrics will call for each button present in the agent live page when hotdesking is enabled.
Change the ’realtime.agent_button_x.channel’ key to the value ’Local/[EM]@from-internal’
This last option is needed only if you use custom agents buttons to dial out extensions and should be repeated for each dial-enabled button. In the code below, a valid example for the button 4 is reported:
realtime.agent_button_4.enabled=true realtime.agent_button_4.caption=Secretary realtime.agent_button_4.url= realtime.agent_button_4.channel=Local/[EM]@from-internal realtime.agent_button_4.ext=200@queuedial
Here should be defined the Asterisk extensions used by QueueMetrics to perform actions triggered from the agent live page.
Add to this file the code reported below:
; extension 32: agent pause with hotdesking (with pause code)
exten => 32,1,Answer
exten => 32,2,NoOp( "QM: Pausing Agent/${AGENTCODE} at extension SIP/${QM_AGENT_LOGEXT} with pause reason ’${PAUSEREASON}’ made by ’${QM_LOGIN}’ " )
exten => 32,3,PauseQueueMember(,SIP/${QM_AGENT_LOGEXT})
exten => 32,4,System( echo "${EPOCH}|${UNIQUEID}|NONE|Agent/${AGENTCODE}|PAUSEREASON|${PAUSEREASON}" >> /var/log/asterisk/queue_log )
exten => 32,5,Hangup
; extension 33: agent unpause with hotdesking
exten => 33,1,Answer
exten => 33,2,NoOp( "QM: Unpausing Agent/${AGENTCODE} at extension SIP/${QM_AGENT_LOGEXT} made by ’${QM_LOGIN}’ " )
exten => 33,3,UnpauseQueueMember(,SIP/${QM_AGENT_LOGEXT})
exten => 33,4,Hangup
; extension 35: agent addqueuemember with hotdesking (for asterisk v1.4+)
exten => 35,1,Answer
exten => 35,2,NoOp( "QM: AddQueueMember (asterisk v1.4+) Agent/${AGENTCODE} at extension SIP/${QM_AGENT_LOGEXT} on queue ${QUEUENAME} made by ’${QM_LOGIN}’" )
exten => 35,3,Macro(queuelog,${EPOCH},${UNIQUEID},NONE,Agent/${AGENTCODE},HOTDESK,SIP/${QM_AGENT_LOGEXT})
exten => 35,4,AddQueueMember(${QUEUENAME},SIP/${QM_AGENT_LOGEXT})
exten => 35,5,Hangup
; extension 37: agent removequeuemember with hotdesking (for asterisk v1.4+)
exten => 37,1,Answer
exten => 37,2,NoOp( "QM: RemoveQueueMember (asterisk v1.4+) Agent/${AGENTCODE} at extension SIP/${QM_AGENT_LOGEXT} on queue ${QUEUENAME} made by ’${QM_LOGIN}’" )
exten => 37,3,RemoveQueueMember(${QUEUENAME},SIP/${QM_AGENT_LOGEXT})
exten => 37,4,HangupPlease note that the ’extensions_queuemetrics.conf’ file that ships with 1.6.1 already has these changes embedded.
In order to have the hotdesking working a complete QueueMetrics restart and Asterisk reload should be performed.