From: dhenry on
Hello,

I have a logging system controlled by a log level (let's say 0 =
disabled, 1 = errors, 2 = error + warnings, ..., 5 = all previous ones
+ debug messages).

A Logging line may look like that:

Log.Write (Text => "Blabla...", Level => 5);

Often, I'll have some formatted text, requiring string conversions and
concatenations. Example:

Log.Write ("Parameter " & To_String (Param_Name) & " Value = " &
Integer'Image (X), Log_Level);

If the Log_Level variable is set to 0 (logging disabled), the
application will still evaluate the Text parameter, before testing the
Level parameter to know if we finally must write it into the file or
not.

I'd like to avoid the string evaluation if the logging is disabled
(because it consumes CPU resources).

A possible solution would be to test the log level before the
procedure call:

if Log_Level > 0 then
Log.Write ("Parameter " & To_String (Param_Name) & " Value = " &
Integer'Image (X), Log_Level);
end if;

But with thousands of logging lines everywhere in my application, it
will make the code quite unreadable and introduces a lot of if-test
pollution (and it's really boring to write).

In a language like C or C++, I would use the preprocessor:
#define LOG(text, level) if (level > 0) { log.write (text, level) }

I'm wondering how in Ada 95 and/or Ada 2005 I could write such a
logging system (if it's possible). The goal is that logging should not
be too intrusive in my code.

Yours,
David.
From: Dmitry A. Kazakov on
On Tue, 18 May 2010 00:48:38 -0700 (PDT), dhenry wrote:

> I'd like to avoid the string evaluation if the logging is disabled
> (because it consumes CPU resources).
>
> A possible solution would be to test the log level before the
> procedure call:
>
> if Log_Level > 0 then
> Log.Write ("Parameter " & To_String (Param_Name) & " Value = " &
> Integer'Image (X), Log_Level);
> end if;

I am using this way, but rather as:

if Log.Level in Warning..Severe then
Log.Write (...);
end if;

> But with thousands of logging lines everywhere in my application, it
> will make the code quite unreadable and introduces a lot of if-test
> pollution (and it's really boring to write).

I don't see much more pollution than a call to Log.Write has already
inflicted.

> I'm wondering how in Ada 95 and/or Ada 2005 I could write such a
> logging system (if it's possible). The goal is that logging should not
> be too intrusive in my code.

AFAIK, there is no way.

But if I were to make a proposal, then I would do the useless pargma Assert
useful. E.g.

pragma Assert (Condition, Message);

[Change]
Dynamic semantics: Message is evaluated only if Condition is evaluated to
true. Then a user-defined assertion handler is called, if any (to be set by
Assertion_Policy). Otherwise Assertion_Error is propagated.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Dmitry A. Kazakov on
On Tue, 18 May 2010 10:15:48 +0200, Dmitry A. Kazakov wrote:

> [Change]
> Dynamic semantics: Message is evaluated only if Condition is evaluated to
> true. Then a user-defined assertion handler is called, if any (to be set by
^^^^
False, of course


--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: stefan-lucks on
On Tue, 18 May 2010, dhenry wrote:

[...]
> Log.Write ("Parameter " & To_String (Param_Name) & " Value = " &
> Integer'Image (X), Log_Level);
>
> If the Log_Level variable is set to 0 (logging disabled), the
> application will still evaluate the Text parameter, before testing the
> Level parameter to know if we finally must write it into the file or
> not.
[...]
> if Log_Level > 0 then
> Log.Write ("Parameter " & To_String (Param_Name) & " Value = " &
> Integer'Image (X), Log_Level);
> end if;
>
> But with thousands of logging lines everywhere in my application, it
> will make the code quite unreadable and introduces a lot of if-test
> pollution (and it's really boring to write).

How about the following?

package Log is

type Level_Type is range 0 .. 5;

procedure Write(Message: String;
Log_Level: Level_Type);

-- The above is, what you have, so far.
-- The stuff below is new:

procedure Write(Message_1: String;
Message_2: String;
Log_Level: Level_Type);
-- if Log_Level>0 then
-- Write(Message_1 & Message_2, Log_Level);
-- end if

procedure Write(Message_1: String;
Message_2: String;
Message_3: String
Log_Level: Level_Type);
-- if Log_Level>0 then ...

...

end Log;

If you are willing to change the order of your parameters, one flexible
Write-procedure would suffice:

with Log;

package Flexi_Log is

procedure Write(Log_Level: Log.Level_Type;
Message_1: String := "";
Message_2: String := "";
Message_3: String := "";
...
Message_9: String := "");
-- if Log_Level>0 then
-- Log.Write(Message_1 & Message_2 & ... , Log_Level);
-- end if;

end Flexi_Log;

In any case, the number of different strings to make one message is
constant. The constant is under your control, but it is constant. If your
support goes for up to, say, Message_9, a message for the log which
consists of 10 or more parts may still trouble you. So change the constant
sufficiently large that it doesn't trouble you too often ...

I hope that helps!

Stefan


--
------ Stefan Lucks -- Bauhaus-University Weimar -- Germany ------
Stefan dot Lucks at uni minus weimar dot de
------ I love the taste of Cryptanalysis in the morning! ------

From: Gautier write-only on
> gcc -S -gnatn test_opti_inline.adb

Errrh! -O2 was missing there.
It's gcc -S -O2 -gnatn test_opti_inline.adb
G.