0

I've got an application receiving messages from different sources (chat rooms and private chats). Multiple instances of the application can be opened, and the final result should be something similar to the following scenario:

Desired data flow

Currently, each application saves logs in a directory which name is the account used to log on the chat server; while it's not a problem for private chat sources (unique for each application instance), it's useless to have the same logs saved multiple times, concerning common chat rooms. Logs are saved in plain text format, so they can be accessed and read without being routed through the application.

If I don't save the logs in separate folders, I might get I/O exceptions due to accessing the same file simultaneously from multiple processes, and I'd need to verify whether the line about to be saved hasn't already been written by other applications. I need to optimize the whole operation and try to maintain code readability.

Besides, my current approach for writing lines is the following:

public void Write(string message)
    {
        using (var writer = new StreamWriter(_fileName, File.Exists(_fileName)))
            writer.WriteLine(message);
    }

Which, considering that logs are constantly written, might not be the most efficient solution.

Summed up, my questions are:

  • How do I create an unique log folder/database, maintaining their format (plain text) but solving the aforementioned duplicates/access problem?
  • How do I improve, if possible, the writing method? Remember that logs need to be constantly written, but that closing the StreamWriter when the application exits would not be a proper solution, as the application is meant to run for a long time.

Thank you.

user1098567
  • 339
  • 3
  • 9
  • Why are you not using a logging framework such as log4net? – Aaron McIver Dec 21 '11 at 14:48
  • @AaronMcIver: Although that's a fair point, it might not help with getting around multiple instances writing non-duplicate data into a single file. – Roman Dec 21 '11 at 14:51
  • 1
    Your sketch suggests, that duplicate logs from Chatroom 1 will only be seen as a duplicate in the log repository. Is that the case? If yes, you will not be able to dedupe them with two uncoupled app instances. – Eugen Rieck Dec 21 '11 at 14:51
  • @Aaron Mclver: I haven't looked at it; my application is meant to be extremely portable and I'd prefer not to use external libraries. – user1098567 Dec 21 '11 at 14:52
  • @EugenRieck: I apologize for not having specified it, but the sketch suggests the desired result, not the actual one. – user1098567 Dec 21 '11 at 14:54
  • 1
    external dependencies aren't a huge factor in portability. and libraries like log4net and other xcopy deployable binaries certanly don't deter portability. – Jason Meckley Dec 21 '11 at 15:15

4 Answers4

1

I would come up with a simple solution, which might be appropriate for your needs, not entirely sure though.

My approach would be to use a single file for each chat session/room. If such a session is started, the application tries to create/open that file and creates a write lock for that file. If it gets an IOException (because the file is locked) it can simply skip logging completely.

Nappy
  • 3,016
  • 27
  • 39
  • Seems a fairly easy and appropriate solution, except that I might implement a different lock to avoid relaying on systematic exception handling. Thanks! – user1098567 Dec 21 '11 at 16:05
0

To be honest, if I were you I would be looking at already exising open source frameworks e.g. NLog. It's fast enough and supports asynchronous logging so it should do exactly what your looking for.

James
  • 80,725
  • 18
  • 167
  • 237
0

Not sure, if I should write this as an answer or a comment, but might need the room:

You mentioned your sketch showing the desired result, but as I said this will prevent you from deduping if you don't couple the instances. So here is what I would suggest:

  • You create two applications: LogWriter, which is a singleton and sits at the bottom of your sketch
  • LogProcessor, which is the application instances in your sketch.
  • Upon startup of a LogProcessor instance, it spawns a LogWriter or connects to it, if it is already running.
  • LogProcessor handles the incoming log requests, maybe preprocesses them if you need to do so, then sends them on to the LogWriter as a tuple of Timestamp, ChatroomID, UserID (need not be unique), text, and maybe a hash for easier deduping. Calculating the hash in the instances makes better use of multiple cores
  • The LogWriter keeps a hanging list sorted by timestamp, containing the hashes, so it is able to quickly discard the duplicate items
  • For the rest of the items, LogWriter determines the logfile path. If a stream is already open to that path, it writes the item out, updates the LastUsed Timestamp on that stream and is done
  • If no stream is open, LogWriter opens one, then writes.
  • If the max number of streams is reached, it closes the oldest stream (as by the above mentioned LastUsed Timestamp) and opens the needed new stream instead.
Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
0

Perhaps change your application design in such a way that a logger is attached to a chat room not to a user.

When users enter a chat room, the chatroom will pass a logger object to the users.

This way all users will use the same logger. The problem then becomes: 1 consumer (the logger), and multiple producers (all those users who want to log).

See my reply to this post:

Write to FileOutputStream from multiple threads in Java

here

https://stackoverflow.com/a/8422621/1007845

Community
  • 1
  • 1
Adrian
  • 5,603
  • 8
  • 53
  • 85