Chapter 1. Dialplan Fundamentals

Introduction

This chapter is designed to show you some fundamental dialplan usage concepts that we use in nearly every dialplan. We’ve developed these recipes to show you how we’ve found the usage of these dialplan applications to be of the greatest value and flexibility.

Counting and Conditionals

Problem

You need to perform basic math—such as increasing a counting variable—and do it using a conditional statement.

Solution

In many cases you will need to perform basic math, such as when incrementing the counter variable when performing loops. To increase the counter variable we have a couple of methods which are common. First, we can use the standard conditional matching format in Asterisk:

[CounterIncrement]
exten => start,1,Verbose(2,Increment the counter variable)

; Set the initial value of the variable
   same => n,Set(CounterVariable=1)
   same => n,Verbose(2,Current value of CounterVariable is: ${CounterVariable})

; Now we can increment the value of CounterVariable
   same => n,Set(CounterVariable=$[${CounterVariable} + 1])
   same => n,Verbose(2,Our new value of CounterVariable is: ${CounterVariable})

   same => n,Hangup()

Alternatively, in versions of Asterisk greater than and including Asterisk 1.8, we can use the INC() dialplan function:

[CounterIncrement]
exten => start,1,Verbose(2,Increment the counter variable)

; Set the inital value of the variable
   same => n,Set(CounterVariable=1)
   same => n,Verbose(2,Current value of CounterVariable is: ${CounterVariable})

; Now we can increment the value of CounterVariable
   same => n,Set(CounterVariable=${INC(CounterVariable)})
   same => n,Verbose(2,Our new value of CounterVariable is: ${CounterVariable})

   same => n,Hangup()

Additionally, we can use the IF() function to determine whether we should be incrementing the value at all:

[CounterIncrement]
exten => start,1,Verbose(2,Increment the counter variable)

; Set the inital value of the variable
   same => n,Set(CounterVariable=1)
   same => n,Verbose(2,Current value of CounterVariable is: ${CounterVariable})

; Here we use the RAND() to randomly help us determine whether we should increment
; the CounterVariable.  We've set a range of 0 through 1, which we'll use
; as false (0) or true (1)
   same => n,Set(IncrementValue=${RAND(0,1)})

; Now we can increment the value of CounterVariable if IncrementValue returns 1
   same => n,Set(CounterVariable=${IF($[${IncrementValue} = 1]?
${INC(CounterVariable)}:${CounterVariable})
   same => n,Verbose(2,Our IncrementValue returned: ${IncrementValue})
   same => n,Verbose(2,Our new value of CounterVariable is: ${CounterVariable})

   same => n,Hangup()

Discussion

The incrementing of variables in Asterisk is one of the more common functionalities you’ll encounter, especially as you start building more complex dialplans where you need to iterate over several values. In versions of Asterisk prior to 1.8, the most common method for incrementing (and decrementing) the value of a counter variable was with the use of the dialplan conditional matching format, which we explored in the first example.

With newer versions of Asterisk, the incrementing and decrementing of variables can be done using the INC() and DEC() dialplan functions respectively. The use of the INC() and DEC() functions requires you to specify only the name of the variable you want to change, and a value is then returned. You do not specify the value to change, which means you would provide ${INC(CounterVariable)}, not ${INC(${CounterVariable})}. If the value of ${CounterVariable} returned 5, then INC() would try and increment the value of the channel variable 5, and not increment the value of 5 to the value 6.

Note

Of course, you could pass ${MyVariableName} to the INC() function, and if the value of ${MyVariableName} contained the name of the variable you actually wanted to retrieve the value of, increment, and then return, you could do that.

It should also be explicitly stated that INC() and DEC() do not modify the original value of the variable. They simply check to see what the current value of the channel variable is (e.g., 5), increment the value in memory, and return the new value (e.g., 6).

In our last code block, we’ve used the RAND() dialplan function to return a random 0 or 1 and assign it to the IncrementVariable channel variable. We then use the value stored in ${IncrementVariable} to allow the IF() dialplan function to return either an incremented value or the original value. The IF() function has the syntax:

${IF($[...conditional statement...]?true_return_value:false_return_value)}

The true or false return values are optional (although you need to return something in one of them). In our case we set our true_return_value to ${INC(CounterVariable)}, which would return an incremented value that would then be assigned to the CounterVariable channel variable. We set the false_return_value to ${CounterVariable}, which would return the original value of ${CounterVariable} to the CounterVariable channel variable, thereby not changing the value.

What we’ve described are common methods for incrementing (and decrementing) channel variables in the dialplan that we’ve found useful.

Looping in the Dialplan

Problem

You need to perform an action several times before continuing on in the dialplan.

Solution

A basic loop which iterates a certain number of times using a counter can be created in the following manner:

[IteratingLoop]
exten => start,1,Verbose(2,Looping through an action five times.)
   same => n,Set(X=1)
   same => n,Verbose(2,Starting the loop)
   same => n,While($[${X} <= 5])
   same => n,Verbose(2,Current value of X is: ${X})
   same => n,Set(X=${INC(X)})
   same => n,EndWhile()
   same => n,Verbose(2,End of the loop)
   same => n,Hangup()

We could build the same type of counter-based loop using the GotoIf() application as well:

[IteratingLoop]
exten => start,1,Verbose(2,Looping through an action five times.)
   same => n,Set(X=1)
   same => n,Verbose(2,Starting the loop)
   same => n(top),NoOp()
   same => n,Verbose(2,Current value of X is: ${X})
   same => n,Set(X=${INC(X)})
   same => n,GotoIf($[${X} <= 5]?top)
   same => n,Verbose(2,End of the loop)
   same => n,Hangup()

Sometimes you might have multiple values you want to check for. A common way of iterating through several values is by saving them to a variable and separating them with a hyphen. We can then use the CUT() function to select the field that we want to check against:

[LoopWithCut]
exten => start,1,Verbose(2,Example of a loop using the CUT function.)
   same => n,Set(Fruits=Apple-Orange-Banana-Pineapple-Grapes)
   same => n,Set(FruitWeWant=Pineapple)
   same => n,Set(X=1)
   same => n,Set(thisFruit=${CUT(Fruits,-,${X})})
   same => n,While($[${EXISTS(${thisFruit})}])
   same => n,GotoIf($[${thisFruit} = ${FruitWeWant}]?GotIt,1)
   same => n,Set(X=${INC(X)})
   same => n,thisFruit=${CUT(Fruits,-,${X})})
   same => n,EndWhile()

; We got to the end of the loop without finding what we were looking for.
   same => n,Verbose(2,Exiting the loop without finding our fruit.)
   same => n,Hangup()

; If we found the fruit, then the GotoIf() will get us here.
exten => GotIt,1,Verbose(2,We matched the fruit we were looking for.)
   same => n,Hangup()

Discussion

We’ve explored three different blocks of code which show common ways of performing loops in the Asterisk dialplan. We’ve shown two ways of performing a counting-based loop, which lets you iterate through a set of actions a specified number of times. The first method uses the While() and EndWhile() applications to specify the bounds of the loop, with the check happening at the top of the loop. The second method uses the GotoIf() application to check whether the loop continues at the bottom of the loop block.

The third loop we’ve shown uses the CUT() dialplan function to move through fields in a list of words that we check for in our loop. When we find what we’re looking for, we jump to another location in the dialplan (the GotIt extension), where we can then continue performing actions knowing we’ve found what we’re looking for. If we iterate through the loop enough times, the channel variable thisFruit will contain nothing, and the loop will then continue at the EndWhile() application, falling through to the rest of the priorities below it. If we get there, we know we’ve fallen out of our loop without finding what we’re looking for.

There are other variations on these loops, such as with the use of the ContinueWhile() and ExitWhile() applications, and the method with which we search for data can also be different, such as with the use of the ARRAY() and HASH() dialplan functions, which are useful when returning data from a relational database using func_odbc.

Controlling Calls Based on Date and Time

Problem

When receiving calls in your auto-attendant, you sometimes need to redirect calls to a different location of the dialplan based on the date and/or time of day.

Solution

[AutoAttendant]
exten => start,1,Verbose(2,Entering our auto-attedant)
   same => n,Answer()
   same => n,Playback(silence/1)

; We're closed on New Years Eve, New Years Day, Christmas Eve, and Christmas Day
   same => n,GotoIfTime(*,*,31,dec?holiday,1)
   same => n,GotoIfTime(*,*,1,jan?holiday,1)
   same => n,GotoIfTime(*,*,24,dec?holiday,1)
   same => n,GotoIfTime(*,*,25,dec?holiday,1)

; Our operational hours are Monday-Friday, 9:00am to 5:00pm.
   same => n,GotoIfTime(0900-1700,mon-fri,*,*?open,1:closed,1)

exten => open,1,Verbose(2,We're open!)
   same => n,Background(custom/open-greeting)
...


exten => closed,1,Verbose(2,We're closed.)
   same => n,Playback(custom/closed-greeting)
   same => n,Voicemail(general-mailbox@default,u)
   same => n,Hangup()

exten => holiday,1,Verbose(2,We're closed for a holiday.)
   same => n,Playback(custom/closed-holiday)
   same => n,Voicemail(general-mailbox@default,u)
   same => n,Hangup()

We don’t just need to use the GotoIfTime() application in an auto-attendant. Sometimes we want to forward calls to people based on time, such as when IT staff is not in the office on weekends, but are on call:

[Devices]
exten => 100,1,Verbose(2,Calling IT Staff.)
   same => n,GotoIfTime(*,sat&sun,*,*?on_call,1)
   same => n,Dial(SIP/itstaff,30)
   same => n,Voicemail(itstaff@default,u)
   same => n,Hangup()

exten => on_call,1,Verbose(2,Calling On-Call IT Staff.)
   same => n,Dial(SIP/myITSP/4165551212&SIP/myITSP/2565551212,30)
   same => n,Voicemail(itstaff@default,u)
   same => n,Hangup()

Discussion

At the top of our auto-attendant, we Answer() the call and Playback(silence/1) which are standard actions prior to playing back prompts to the caller. This eliminates the first few milliseconds of a prompt from being cut off. After that, we then start our checks with the GotoIfTime() application. We start with specific matches first (such as particular days of the week) before we do our more general checks, such as our 9:00 a.m. to 5:00 p.m., Monday to Friday checks. If we did it the other way around, then we’d be open Monday–Friday, 9:00 a.m.–5:00 p.m. on holidays.

The GotoIfTime() application contains five fields (one of which is optional; we’ll discuss it momentarily). The fields are: time range, days of the week, days of the month, and months. The fifth field, which is optional and specified after the months field, is the timezone field. If you are servicing multiple timezones, you could use this to have different menus and groups of people answering the phones between 9:00 a.m. and 5:00 p.m. for each timezone.

On holidays, we have a separate part of the menu in which we play back a prompt and then send the caller to Voicemail(), but you could, of course, send the caller to any functionality that you wish. We’ve used the asterisk (*) symbol to indicate that at any time of the day and any day of the week, but only on the 31st day of December, should calls go to the holiday extension. We then specify in the same manner for the 1st of January, the 24th of December, and the 25th of December, all of which are handled by the holiday extension.

After we’ve determined it’s not a holiday, then we perform our standard check to see if we should be using the open extension—which we’ll use when the office is open—or the closed extension—which plays back a prompt indicating we’re closed and to leave a message in the company Voicemail().

In our second block of code, we’ve also shown how you could use the GotoIfTime() to call IT staff on the weekends (Saturday and Sunday, any time of the day). Normally, people would call the extension 100, and if it is Monday to Friday, they would be directed to the SIP device registered to [itstaff] in sip.conf. Of course, since the people reading and implementing this probably are IT staff, there is a good chance you will alter this to call at hours which are more sane.

Authenticating Callers

Problem

You need to authenticate callers prior to moving on in the dialplan.

Solution

[Authentication]
exten => start,1,Verbose(2,Simple Authenicate application example)
   same => n,Playback(silence/1)
   same => n,Authenticate(1234)
   same => n,Hangup()

Here is a slightly modified version that sets the maxdigits value to 4, thereby not requiring the user to press the # key when done entering the password:

[Authentication]
exten => start,1,Verbose(2,Simple Authenicate application example)
   same => n,Playback(silence/1)
   same => n,Authenticate(1234,,4)
   same => n,Hangup()

By starting our password field with a leading forward slash (/), we can utilize an external file as the source of the password(s):

[Authentication]
exten => start,1,Verbose(2,Simple Authenicate application example)
   same => n,Playback(silence/1)
   same => n,Authenticate(/etc/asterisk/authenticate/passwd_list.txt)
   same => n,Hangup()

If we use the d flag, Asterisk will interpret the path provided as a database key in the Asterisk DB instead of a file:

[Authentication]
exten => start,1,Verbose(2,Simple Authenicate application example)
   same => n,Playback(silence/1)
   same => n,Authenticate(/authenticate/password,d)
   same => n,Hangup()

We can insert and modify the password we’re going to use in the Asterisk database using the Asterisk CLI:

*CLI> database put authenticate/password 1234 this_does_not_matter
Updated database successfully

*CLI> database show authenticate
/authenticate/password/1234                       : this_does_not_matter     
1 result found.

A neat modification for temporary passwords in the Asterisk Database (AstDB) is by adding the r flag along with the d flag to remove the password from the AstDB upon successful authentication:

[Authentication]
exten => start,1,Verbose(2,Simple Authenicate application example)
   same => n,Playback(silence/1)
   same => n,Authenticate(/authenticate/temp_password,dr)
   same => n,Hangup()

After we insert the password into the database, we can use it until an authentication happens, and then it is removed from the database:

*CLI> database put authenticate/temp_password 1234 this_does_not_matter
Updated database successfully

*CLI> database show authenticate

Discussion

The Authenticate() dialplan application is quite basic at its core; a password is provided to the dialplan application, and that password must be entered correctly to authenticate and continue on in the dialplan. The Authenticate() application is one of the older applications in Asterisk, and it shows that by the number of available options for something that should be an almost trivial application. While much of the functionality provided by the Authenticate() application can be done in the dialplan using dialplan functions and other applications, it is somewhat nice to have much of the functionality contained within the application directly.

We’ve provided examples of some of this functionality in the Solution section. We started off with a simple example of how you can provide a password to Authenticate() and then require that password to be entered before continuing on in the dialplan. The first example required the user to enter a password of 1234 followed by the # key to signal that entering of digits was complete. Our second example shows the use of the maxdigits field, with a value of 4, to not require the user to press the # key when done entering the password.

We went on to show how you could provide a path to the password field, which would allow you to utilize a file for authentication. Using an external file can be useful if you want to use an external script to rotate the password fairly often. One of the particular uses we can think of would be the SecurID system, by RSA, which uses cards that contain a number that rotates over a period of time to be synchronized with a centralized server that is using the same algorithm to generate passwords. If you wanted to tie this system into Asterisk, you could have a script that rotated the key on a timely basis.

Instead of using a file for the location of the password, there is an option that lets you use the Asterisk DB. With the d flag, we can tell Authenticate() that the path we’re providing is that of a family/key relationship in the AstDB. An additional flag, r, can also be provided that removes the key from the database upon successful authentication, which provides a method for one-time-use passwords.

The Authenticate() application is a general purpose authentication mechanism which provides a base layer without taking into consideration which user or caller is attempting to authenticate. It would be fairly straightforward, however, to add that layer with some additional dialplan, or you could even utilize some of the dialplan we’ve provided in this book.

Authenticating Callers Using Voicemail Credentials

Problem

You need to provide an authentication mechanism in your dialplan, but wish to use the credentials already in place for retrieving voicemail.

Solution

[Authentication]
exten => start,1,Verbose(2,Attempting to authenticate caller with voicemail creds.)
   same => n,Playback(silence/1)

; This is where we do our authentication
   same => n,VMAuthenticate(@default)

   same => n,Verbose(2,The caller was authenticated if we execute this line.)
   same => n,Goto(authenticated,1)

exten => authenticated,1,Verbose(2,Perform some actions.)
   ; would contain 'default'.
   same => n,Verbose(2,Value of AUTH_CONTEXT: ${AUTH_CONTEXT})
   ; mailbox '100' was authenticated.
   same => n,Verbose(2,Value of AUTH_MAILBOX: ${AUTH_MAILBOX})
   same => n,Playback(all-your-base)
   same => n,Hangup()

If you wanted to explicitly define the mailbox to authenticate against, you could place the extension number to authenticate with in front of the voicemail context:

same => n,VMAuthenticate(100@default)

And if you don’t like the introductory prompts that VMAuthenticate() plays, you could modify your dialplan to play a different initial prompt by adding the s flag:

same => n,Playback(extension)
same => n,VMAuthenticate(@default,s)

Also, if we wanted to provide the option of letting someone skip out of authenticating altogether, and perhaps speak with an operator, we could provide the a extension in our context, which allows the user to press * to jump to the a extension:

[Authentication]
exten => start,1,Verbose(2,Attempting to authenticate caller with voicemail creds.)
   same => n,Playback(silence/1)

; This is where we do our authentication
   same => n,VMAuthenticate(@default)

   same => n,Verbose(2,The caller was authenticated if we execute this line.)
   same => n,Goto(authenticated,1)

exten => a,1,Verbose(2,Calling the operator.)
   same => n,Dial(SIP/operator,30)
   same => n,Voicemail(operator@default,u)
   same => n,Hangup()

Discussion

The VMAuthenticate() application provides us a useful tool for authenticating callers using credentials the user already knows, and uses often. By using the caller’s own voicemail box number and password, it is one less piece of authentication information to be memorized on the part of the caller. It also provides a centralized repository of credentials the administrator of the system can control and enforce to be secure. Since we have the ability to skip playing back the initial prompts, we could obtain the mailbox number used for authentication in several ways: by asking the user to provide it using another application or a lookup from a database, or simply by dialing it from the phone and performing a pattern match.

Another advantage is providing the ability to quit out of the authentication mechanism and be connected to a live agent, who can then perform the authentication for the caller using information stored in his database and then transfer the caller to the part of the system which would have required her authentication credentials.

Authenticating Callers Using Read()

Problem

You want to authenticate callers using a custom set of prompts and logic.

Solution

A basic system which uses a static pin number for authentication:

[Read_Authentication]
exten => start,1,NoOp()
   same => n,Playback(silence/1)
   same => n,Set(VerificationPin=1234)   ; set pin to verify against

   same => n,Set(TriesCounter=1)         ; counter for login attempts
   same => n(get_pin),Read(InputPin,enter-password)  ; get a pin from
                                                     ; from the caller

; Check if the pin input by the caller is the same as our verification pin.
   same => n,GotoIf($["${InputPin}" = "${VerificationPin}"]?pin_accepted,1)

; Increment the TriesCounter by 1
   same => n,Set(TriesCounter=${INC(TriesCounter)})
   same => n,GotoIf($[${TriesCounter} > 3]?too_many_tries,1)
   same => n,Playback(vm-incorrect)
   same => n,Goto(get_pin)

exten => pin_accepted,1,NoOp()
   same => n,Playback(auth-thankyou)
   same => n,Hangup()

exten => too_many_tries,1,NoOp()
   same => n,Playback(vm-incorrect)
   same => n,Playback(vm-goodbye)
   same => n,Hangup()

We can modify the dialplan slightly to provide greater security by requiring an account code and a pin, which can be loaded from the AstDB:

[Read_Authentication]
exten => start,1,NoOp()
   same => n,Playback(silence/1)

; Set a couple of counters for login attempts
   same => n,Set(TriesCounter=1)
   same => n,Set(InvalidAccountCounter=1)

; Request the access code (account code)
   same => n(get_acct),Read(InputAccountNumber,access-code)
; make sure we have an account number
   same => n,GotoIf($[${ISNULL(${InputAccountNumber})}]?get_acct)

; Request the password (pin)
   same => n(get_pin),Read(InputPin,vm-password)

; Check the database to see if a password exists for the account number entered
   same => n,GotoIf($[${DB_EXISTS(access_codes/${InputAccountNumber})}]?
have_account,1:no_account,1)

; If a pin number exists check it against what was entered
exten => have_account,1,NoOp()
   same => n,Set(VerificationPin=${DB_RESULT})
   same => n,GotoIf($["${InputPin}" = "${VerificationPin}"]?pin_accepted,1)
   same => n,Set(TriesCounter=${INC(TriesCounter)})
   same => n,GotoIf($[${TriesCounter} > 3]?too_many_tries,1)
   same => n,Playback(vm-incorrect)
   same => n,Goto(start,get_pin)

; If no account exists, request a new access code be entered
exten => no_account,1,NoOp()
   same => n,Playback(invalid)
   same => n,Set(InvalidAccountCounter=${INC(InvalidAccountCounter)})
   same => n,GotoIf($[${InvalidAccountCounter} > 3]?too_many_tries,1)
   same => n,Goto(start,get_acct)

; Account and pin were verified
exten => pin_accepted,1,NoOp()
   same => n,Playback(auth-thankyou)
   same => n,Hangup()

; Sorry, too many attempts to login. Hangup.
exten => too_many_tries,1,NoOp()
   same => n,Playback(vm-incorrect)
   same => n,Playback(vm-goodbye)
   same => n,Hangup()

We added the access code (555) and pin (1234) to the AstDB using the following command:

*CLI> database put access_codes 555 1234

Discussion

If you’ve already looked at the solutions in Recipes and , what you’ll immediately notice is that the other solutions are quite a bit more compact. The main reason is that all of the loop control is handled within the dialplan application, whereas in this case we’re defining the loop control with dialplan logic and handling the number of loops and what to do on failure ourselves. With greater control comes greater complexity, and that is illustrated in the examples provided. The advantage to this greater level of verbosity is the control at each step: which prompts are played, when they’re played, and how often they are played. We also get to control the method of authentication.

In our first example, we showed a fairly basic authentication method which simply asked the caller for a password that we statically defined within the dialplan.[1] After entering the pin, we check what the user entered against what we set in the VerificationPin channel variable. If the numbers do not match, we increment a counter, test to see the number has increased to greater than 3, and, if not, request that the caller re-enter her pin. If the number of tries exceeds 3, then we play a prompt saying goodbye and hang up.

Our second example was expanded to include both an access code (account code) and a password (pin) which we’ve written to the AstDB. When the caller enters the dialplan, we request an access code and a password. We then check the AstDB using DB_EXISTS() to determine if the account number exists in the database. If it does not, then we inform the user that the account does not exist using a dialplan defined by the no_account extension. This is followed by a check to determine if the caller has entered an invalid account number and, if so, determine if this has happened more than 3 times, in which case we then disconnect the caller.

If the caller has entered a valid account number, we then handle additional logic in the have_account extension, where we verify the pin number entered against what was returned from the database. If everything is valid, then we play back a prompt in the pin_accepted extension, and hang up the call (although it’s implied additional logic could then be handled now that the caller has been validated).

Because the solutions described contain a lot of dialplan logic and aren’t tied to any particular dialplan application (other than Read(), which we’re using for data input), the solutions could easily be modified to authenticate against other external sources of data. For example, the REALTIME() functions could be used to gather credentials from an LDAP database, or func_odbc could be employed to gather information from a relational database. You could even use CURL() to pass the data collected from the caller to authenticate against a web page which could then return account information to the caller. The possibilities with custom dialplan really are only limited by the problems encountered and solved by your imagination.

Debugging the Dialplan with Verbose()

Problem

You would like to insert debugging into your dialplan that can be enabled on a global, per-device, or per-channel basis.

Solution

Use something like this chanlog GoSub() routine:

[chanlog]
exten => s,1,GotoIf($[${DB_EXISTS(ChanLog/all)} = 0]?checkchan1)
    same => n,GotoIf($[${ARG1} <= ${DB(ChanLog/all)}]?log)
    same => n(checkchan1),Set(KEY=ChanLog/channel/${CHANNEL})
    same => n,GotoIf($[${DB_EXISTS(${KEY})} = 0]?checkchan2)
    same => n,GotoIf($[${ARG1} <= ${DB(${KEY})}]?log)
    same => n(checkchan2),Set(KEY=ChanLog/channel/${CUT(CHANNEL,-,1)})
    same => n,GotoIf($[${DB_EXISTS(${KEY})} = 0]?return)
    same => n,GotoIf($[${ARG1} <= ${DB(${KEY})}]?log)
    same => n(return),Return() ; Return without logging
    same => n(log),Verbose(0,${ARG2})
    same => n,Return()

Discussion

The chanlog GoSub() routine takes two arguments:

ARG1

A numeric log level

ARG2

The log message

Channel logging using this routine will be sent to the Asterisk console at verbose level 0, meaning that they will show up when you want them to, regardless of the current core set verbose setting. This routine uses a different method, values in AstDB, to control what messages show up. See Table 1-1 for the AstDB entries read in by the chanlog routine. If the log level argument is less than or equal to one of these matched entries in the AstDB, then the message will be logged.

Table 1-1. chanlog AstDB entries
FamilyKeyDescription
ChanLog/allThis setting is applied to all channels.
ChanLog/channels/<device>The chanlog routine will also look for an entry that matches the part of the channel name that comes before the -. For example, if the channel name is SIP/myphone-00112233, the routine will look for a key of channels/SIP/myphone.
ChanLog/channels/<full_channel>The chanlog routine also checks for a match against the full channel name.

Once this routine has been added to your dialplan, you can start adding log statements to any extensions on your system. As an example, here is a typical company main menu that now has chanlog messages for each step:

exten => 6000,1,GoSub(chanlog,s,1(1,[${CHANNEL}] Entered main menu))
    same => n,Background(main-menu)
    same => n,WaitExten(5)

exten => 1,1,GoSub(chanlog,s,1(1,[${CHANNEL}] Pressed 1 for Sales))
    same => n,Goto(sales,s,1)

exten => 2,1,GoSub(chanlog,s,1(1,[${CHANNEL}] Pressed 2 for Technical Support))
    same => n,Goto(tech_support,s,1)

exten => 3,1,GoSub(chanlog,s,1(1,[${CHANNEL}] Pressed 3 for Customer Service))
    same => n,Goto(customer_service,s,1)

exten => 4,1,GoSub(chanlog,s,1(1,[${CHANNEL}] Pressed 4 for Training))
    same => n,Goto(training,s,1)

exten => 5,1,GoSub(chanlog,s,1(1,[${CHANNEL}] Pressed 5 for Directory))
    same => n,Directory()

exten => 0,1,GoSub(chanlog,s,1(1,[${CHANNEL}] Pressed 0 for Operator))
    same => n,Goto(operator,s,1)

exten => i,1,GoSub(chanlog,s,1(1,[$CHANNEL}] invalid "${INVALID_EXTEN}"))
    same => n,Goto(6000,1)

exten => t,1,GoSub(chanlog,s,1(1,[${CHANNEL}] Timed out waiting for digit))
    same => n,Goto(6000,1)

To see the output, we will first disable all other verbose messages from Asterisk and then turn on chanlog messages for all channels:

*CLI> core set verbose 0
*CLI> database put ChanLog all 1

Now when someone calls into the main menu, he will see messages like this at the Asterisk console:

[SIP/000411223344-000000b8] Entered main menu
[SIP/000411223344-000000b8] Pressed 4 for Training

See Also

For more information about other types of logging and monitoring of Asterisk systems, see Chapter 24, “System Monitoring and Logging,” of Asterisk: The Definitive Guide (O’Reilly).



[1] Of course we could have defined that as a global variable in the [globals] section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset