5

I have a bash script from which i need to write logs. currently i use block like the one below

#log stop request { local now=$(date +"%a %b %d %Y %H:%M:%S") printf "batch stop request successful. \n" printf " Time :: %s\n" "$now" printf " PID of process :: %s\n" ${PID[0]}" } >> "${mylogfile}" 

where mylogfile variable will have name of the logfile.

The problem with this approach is that when 2 or more instances are running, the logs tends to get messed up with writes from instances coming interleaved.

Please note i used the block thinking that it would result in the log being written to file in one go, thus avoiding the problem.

I have seen logger command from Vivek Gite post. but the problem is it does not write to a file I can specify, rather does so to /var/log/message.

Any help is much appreciated.

Thanks and Regards Sibi

2
  • What happens if you group all the writing into a single printf statement? What happens if you forego the multiple lines for a single message and use just one line for the whole message? Commented Nov 25, 2011 at 18:43
  • Does not work, still i get interleaved messages Commented Nov 25, 2011 at 18:59

4 Answers 4

3

POSIX (IEEE Std 1003.1-2001) does not define the behavior of concurrent write() syscalls sending data to the same file, hence you may obtain different results depending on your platform. You can try to merge all printfs into one in the hope that this will work, but even if it does there is no guarantee that it will in the future or on a different platform.

Rather than using concurrency control and flushing to ensure the writes are sequenced, you can send the messages to a third process which will write your log messages sequentially to a file on behalf on all the processes. In fact, this is what is done with logger and syslog in the post you cited. The logger command does not send the messages to /var/log/messages. It sends the log messages to syslog which can be configured to save the log messages anywhere you like. Changing this configuration usually requires administrative privileges, though.

If you can't or don't want to use syslog, you can also use netcat as a logging server. Run this to multiplex all incoming log messages from all scripts into the file (this job should remain running in the background all the time, you can also run it in a screen):

nc -luk localhost 9876 > shared_log_file & 

(port 9876 is just an example) and log in each script this way:

printf "Log message\n" > /dev/udp/localhost/9876 

You can also use a custom UDP server instead of netcat (e.g. like this one).

Sign up to request clarification or add additional context in comments.

5 Comments

Hi Adam, thanks for the great explanation. Any pointer on where i can get a sample for logger would be great. I tried the net but with no results.
I still think that's needlessly complex when he could just group his related printfs in a sub-shell and redirect its output, since he's not looking for a specific kind of logging behavior (e.g. through syslog(3)), he just wants "transactional" writes.
hi Adam and jkh, JKH is right am looking for transactional write. Thats to both of you for helping me
My understanding is that his printfs happen in unrelated processes. If he could group all these processes run under a common shell, then your solution is indeed simpler and does the job.
My printf happen in unrelated processes. Mine is a batch run script and more than one batch can be run in parallel writing to same log file.
2

The block does not group the printfs in any way, as you have discovered. What you could do, however, is do all of those commands in a sub-shell (between parenthesis) and then redirect the sub-shell's output!

Comments

1

Depending on the nature of your logging, it might be well-suited for the system log instead of its private logfile. In this case, the logger program could be a very good alternative to hand-rolled logging.

2 Comments

I like this suggestion. There was no mention as to why the system log can't be used.
Hi phasetwenty, I have a requirement to log on a file at custom location.
1

Trying putting the 3 printfs as one string, i.e.

printf "Epsilon batch stop request successful. \n Time :: %s\n PID of process :: %s\n" "$now" ${PID[0]} 

You may still get interleaving.

Why not start each instance with its own log file, using the ${PID} on your logfile name to keep them separate? i.e.

 >> "${mylogfile}.${PID}" 

Edit

As you're hoping for logger, check out the man page :

logger(1) - Linux man page Name logger - a shell command interface to the syslog(3) system log module Synopsis logger [-isd] [-f file] [-p pri] [-t tag] [-u socket] [message ...] Description Logger makes entries in the system log. It provides a shell command interface to the syslog(3) system log module. Options: -i' Log the process id of the logger process with each line. -s' Log the message to standard error, as well as the system log. -f file Log the specified file. ..... 

That looks like what you want.

I hope this helps.

1 Comment

hi shellter , i have a restriction that I must log to one file. Is there a way I can use logger to write to a custom file?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.