Truncated error report in erlang - erlang

Warning: erlang n00b ahead.
I'm trying to get a grasp of erlang, and just trying a basic hello world application with cowboy. I'm simulating an error, basically returning an invalid value somewhere in my code, and trying to interpret the error, which is:
=ERROR REPORT==== 11-Jul-2013::15:45:00 ===
Error in process <0.167.0> with exit value: {{try_clause,{ok, {http_req,#Port<0.3619>,ranch_tcp,keepalive,<0.167.0>,<<3 bytes>>,'HTTP/1.1', {{127,0,0,1},60312},<<9 bytes>>,undefined,8081,<<1 byte>>,undefined,<<0 bytes>>,undefined,[],[{<<10 bytes>>,<<11 bytes>>},{<<4 bytes>>,<<14 bytes>>},{<<6 bytes>>,<<3 bytes>>}],[],undefined,[],waiting,undefined,<<0 bytes>>,false,waiting,[],<<0 bytes>>,undefined}}}, [{cowboy_handler,handler_init,4,[...
I've setup my application with rebar, and I'm running it with:
erl -pa ebin deps/*/ebin -s myapp
As you can see, the error ends with "..." which makes me think it is being truncated. Is there any way to print the full report?
And, is there any way to make it pretty-print it?
Thanks!

What you see is sasl's tty handler. It formats and writes reports to console. During formatting it could cut some useful data.
To avoid it, use error_logger_mf_h handler like this:
Create app.config file to pass some vars to sasl:
[
{sasl, [
{sasl_error_logger, {file, "sasl.log"}},
{errlog_type, all},
{error_logger_mf_dir, "."}, % Log directory
{error_logger_mf_maxbytes, 10485760}, % 10 MB max file size
{error_logger_mf_maxfiles, 5} % 5 files max
]}
].
Then bring erl node with started sasl. Logs will be printed into sasl.log
erl -boot start_sasl -config app.config

The error message is truncated by the Erlang emulator, so there is no way to get the full message from sasl. There is an undocumented switch that you can use to see more of the message. "erl +# 400" truncate at 400 instead of 200 characters.
More details: http://erlang.org/pipermail/erlang-questions/2014-October/081442.html

I believe there is no easy way of telling sasl to not truncate the error report.
What you could do is create your own handler for the reports and log the error in a way that it is not truncated. (e.g. io:format("~p", [Error])).
Look here for an exemple of custom report handler.
And you must add your custom handler as a subscriber of error reports:
error_logger:add_report_handler(?MODULE, []).
Or you could use a logging library that supports error_logger.

Related

Write to the system's standard error in Progress

I am writing a small program in Progress that needs to write an error message to the system's standard error. What ways, simple if at all possible, can I use to print to standard error?
I am using OpenEdge 11.3.
When on Windows (10.2B+) you can use .NET:
System.Console:Error:WriteLine ("This is an error message") .
together with
prowin32 2> stderr.out
Progress doesn't provide a way to write to stderr - the easiest way I can think of is to output-through an external program that takes stdin and echoes it to stderr.
You could look into LOG-MANAGER:WRITE-MESSAGE. It won't log to standard output or standard error, but to a client-specific log. This log should be monitored in any case (specifically if the client is an application server).
From the documentation:
For an interactive or batch client, the WRITE-MESSAGE( ) method writes the log entries to the log file specified by the LOGFILE-NAME attribute or the Client Logging (-clientlog) startup parameter. For WebSpeed agents and AppServer servers, the WRITE-MESSAGE() method writes the log entries to the server log file. For DataServers, the WRITE-MESSAGE() method writes the log entries to the log file specified by the DataServer Logging (-dslog) startup parameter.
LOG-MANAGER:WRITE-MESSAGE("Got here, x=" + STRING(x), "DEBUG1").
Will write this in the log:
[04/12/05#13:19:19.742-0500] P-003616 T-001984 1 4GL DEBUG1 Got here, x=5
There are quite a lot of options regarding the LOG-MANAGER system, what messages to display, where the file is placed, etc.
There is no easy way, but in Unixen you can always do something like this using OUTPUT THROUGH (untested):
output through "cat >&2" no-echo unbuffered.
Alternatively -- and this is tested -- if you just want error messages from a batch-mode program to go to standard out then
output through "tee" ...
...definitely works.

Meaningful auto-generated name for PID for debug purposes?

There's some process started with Task.Supervisor.start_child, if something went wrong Elixir (Erlang) prints error message like this:
23:56:06.257 [error] Task #PID<0.216.0> started from #PID<0.137.0> terminating
It's hard to understand what process crashed. I wonder if there's a way to give a meaningful auto-generated names to processes? So the error will be more descriptive, like:
23:56:06.257 [error] Task #PID-REQUEST-HANDLER<0.216.0> started from #PID-SOCKET-LOOP<0.137.0> terminating
You can name each desired process with register/2 function:
Erlang:
register(RegName, PidOrPort) -> true
Elixir:
register(pid | port, atom) :: true
Then what you need is implementing your logger to use process_info(PID, registered_name) function to get the registered name of the desired process by its PID and formatting the log text with that name.
Update: Also it is good to know that error_logger is an event manager (gen_event). Error, warning, crash, progress and info events are sent to the error logger from the Erlang runtime system and the different Erlang/OTP applications. It has some default event handlers, as well as sasl which is an OTP application that adds more logging features to your application. You can also write your own event handler and add it to error_logger event manager.

Erlang Tracing: Failed to open trace

I am currently observing traces of Erlang programs (using the Erlang:trace function), specifically the YAWS web server, although my problem may not be limited to this particular program.
Basically, when I attempt to trace some processes and output these traces, the following errors are displayed:
=ERROR REPORT==== 30-Mar-2014::15:48:25 ===
Failed to open trace "trace.<0.4084.0>.traffic": "I/O error"
=ERROR REPORT==== 30-Mar-2014::15:48:25 ===
Failed to open trace "trace.<0.4085.0>.traffic": "I/O error"
=ERROR REPORT==== 30-Mar-2014::15:48:26 ===
Failed to open trace "trace.<0.4086.0>.traffic": "I/O error"
I would appreciate any insight into why this error is occurring and how it can be solved.
Thanks!
EDIT:
This is the code I am using:
looper()->
receive
P-> io:format(" trace msg: ~p ~n", [P])
end,
looper().
tracer2(Pid)->
erlang:trace(Pid,true,[set_on_spawn, send, 'receive']),
looper().
In the cmd, I first enter: ybed_sup:start_link() , then enter tracer2(PID) passing the PID of the process executing the function yaws_server:acceptor as a parameter.
At first, traces are printed correctly, but then this process dies and new processes with the functon yaws_server:acceptor are spawned, after which only the error messages show.
I think you might be confusing Erlang tracing and the tracing capabilities Yaws provides for HTTP requests. They are very different things.
The error you're seeing is caused by Yaws being unable to open the files mentioned in your error messages (for example, trace.<0.4084.0>.traffic), which by default are opened in the logdir specified in the yaws.conf file. You can read more about this near the top of the yaws.conf documentation or in the yaws.conf.5 man page that's part of your Yaws installation.
After your EDIT:
I fully agree with Steve Vinoski answer.
Few notes:
with current logic you "freeze" you shell (cmd), you might want spawn a new process for loop/0 function
if you just would like to simply debug your system try using dbg module
if trace/2 function would fail you would get little different error message; something similar to
** exception error: bad argument
in function erlang:trace/3
called as erlang:trace(a,b,c)
so you can assume code you wrote works just fine
(as far as I understand you) you're tracing process that is spawning yaws acceptor; make sure that you turn on tracing before acceptor is spawned, or pass to your trace2/1 function pid returned from yaws_server:acceptor
And finally make sure that you configuration is ok. From what you are describing your log_dir might not exist (or you could forget to add flag allowing to create folder), or you might not have permission to write in this directory.

Simple program that reads and writes to a pipe

Although I am quite familiar with Tcl this is a beginner question. I would like to read and write from a pipe. I would like a solution in pure Tcl and not use a library like Expect. I copied an example from the tcl wiki but could not get it running.
My code is:
cd /tmp
catch {
console show
update
}
proc go {} {
puts "executing go"
set pipe [open "|cat" RDWR]
fconfigure $pipe -buffering line -blocking 0
fileevent $pipe readable [list piperead $pipe]
if {![eof $pipe]} {
puts $pipe "hello cat program!"
flush $pipe
set got [gets $pipe]
puts "result: $got"
}
}
go
The output is executing go\n result:, however I would expect that reading a value from the pipe would return the line that I have sent to the cat program.
What is my error?
--
EDIT:
I followed potrzebie's answer and got a small example working. That's enough to get me going. A quick workaround to test my setup was the following code (not a real solution but a quick fix for the moment).
cd /home/stephan/tmp
catch {
console show
update
}
puts "starting pipe"
set pipe [open "|cat" RDWR]
fconfigure $pipe -buffering line -blocking 0
after 10
puts $pipe "hello cat!"
flush $pipe
set got [gets $pipe]
puts "got from pipe: $got"
Writing to the pipe and flushing won't make the OS multitasking immediately leave your program and switch to the cat program. Try putting after 1000 between the puts and the gets command, and you'll see that you'll probably get the string back. cat has then been given some time slices and has had the chance to read it's input and write it's output.
You can't control when cat reads your input and writes it back, so you'll have to either use fileevent and enter the event loop to wait (or periodically call update), or periodically try reading from the stream. Or you can keep it in blocking mode, in which case gets will do the waiting for you. It will block until there's a line to read, but meanwhile no other events will be responded to. A GUI for example, will stop responding.
The example seem to be for Tk and meant to be run by wish, which enters the event loop automatically at the end of the script. Add the piperead procedure and either run the script with wish or add a vwait command to the end of the script and run it with tclsh.
PS: For line-buffered I/O to work for a pipe, both programs involved have to use it (or no buffering). Many programs (grep, sed, etc) use full buffering when they're not connected to a terminal. One way to prevent them to, is with the unbuffer program, which is part of Expect (you don't have to write an Expect script, it's a stand-alone program that just happens to be included with the Expect package).
set pipe [open "|[list unbuffer grep .]" {RDWR}]
I guess you're executing the code from http://wiki.tcl.tk/3846, the page entitled "Pipe vs Expect". You seem to have omitted the definition of the piperead proc, indeed, when I copy-and-pasted the code from your question, I got an error invalid command name "piperead". If you copy-and-paste the definition from the wiki, you should find that the code works. It certainly did for me.

How to refine the debugging?

Crash report (SASL) gives more or less where and why a bug happens.
But is it possible to refine this (the function, the line de code, etc) ?
If you can reproduce the fault, the best way to get more information is to put a dbg trace on sections in question and review that output.
dbg:tracer(),dbg:p(all,c),dbg:tpl(Mod,Func,x).
This usually does the trick for me. Replace Mod and Func with whatever module and function you want to debug.
If you are looking for more detailed post-mortem logging then sasl and the error_logger are your friends. There are of course times when SASL does not give you enough info, if this happens a lot in your system you probably should either learn to understand the SASL output better or write your own log handler. It is quite easy to plug-in your own error handler into SASL and output things as you want.
You will however never get line number as that information is destroyed at compilation time and there is no way for the VM to know which line crashed. It does however know which function and possibly with which arguments, given this it is usually possible to find out where things went wrong. Unless you write very long functions, which IMO is bad code smell and a sign that you should refactor your code to smaller functions.
In general, no. The erlang .beam files does not contain the line numbers from the original code, so it is hard to know at what line the problem occurred. I do have a number of macros I use in my project, included as "log.hrl":
-define(INFO(T), error_logger:info_report(T)).
-define(WARN(T), error_logger:warning_report(
[process_info(self(), current_function), {line, ?LINE} | T])).
-define(ERR(T), error_logger:error_report(
[process_info(self(), current_function), {line, ?LINE} | T])).
-define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
[self(),?MODULE,?LINE]++Args)).
-define(DEBUGP(Args), io:format("D(~p:~p:~p) : ~p~n",
[self(),?MODULE,?LINE, Args])).
and this does give you some log lines in the program to hunt for. For debugging I often also use the redbug tool from the eper suite:
https://github.com/massemanet/eper
It allows you to trace in realtime whenever a call happens:
Eshell V5.8.3 (abort with ^G)
1> redbug:start("erlang:now() -> stack;return", [{time, 60*1000}]).
ok
2> erlang:now().
{1297,183814,756227}
17:50:14 <{erlang,apply,2}> {erlang,now,[]}
shell:eval_loop/3
shell:eval_exprs/7
shell:exprs/7
17:50:14 <{erlang,apply,2}> {erlang,now,0} -> {1297,183814,756227}
3>
I hope this helps.

Resources