danieleteti/loggerpro

DelAppender - Argument Out Of Range Exception

vveliu-csw opened this issue · 2 comments

Deleting appender at runtime, raises Argument Out Of Range Exception.

The problem is in the DelAppender procedure:

procedure TCustomLogWriter.DelAppender(const aAppender: ILogAppender);
.......
  for i := 0 to Self.FLoggerThread.FAppendersDecorators.Count - 1 do
    if Self.FLoggerThread.FAppendersDecorators[i].FLogAppender = aAppender then
      Self.FLoggerThread.FAppendersDecorators.Delete(i);
end;

The for statement loops till i = Self.FLoggerThread.FAppendersDecorators.Count - 1 , this is a mistake beacuse if we delete some Appender contained in the list the higher limit of the loop is not re assigned. In a list of 3 items for will loop from 0 to 2 but if we delete an item the loop don't stop so trying to read/write List[2] will raise the exception.

I used a repeat .. until .. statement instead, the loop should stop after succesfull delete (appender is supposed to exists only once in the list).

Modified version of mine:

procedure TCustomLogWriter.DelAppender(const aAppender: ILogAppender);
var
  i: Integer;
  LAppenderDeleted : Boolean;
begin
  i := Self.FLogAppenders.IndexOf(aAppender);
  if i >= 0 then
  begin
    Self.FLogAppenders.Delete(i);
  end;

  if Self.FLoggerThread.FAppendersDecorators.Count = 0 then
  begin
    Exit;
  end;

  i := 0;
  LAppenderDeleted := False;

  repeat
    if Self.FLoggerThread.FAppendersDecorators[i].FLogAppender = aAppender then
    begin
      Self.FLoggerThread.FAppendersDecorators.Delete(i);
      LAppenderDeleted := True;
    end;

    Inc(i);
  until (LAppenderDeleted or (i = Self.FLoggerThread.FAppendersDecorators.Count));
end;

Plus. I removed the above code beacuse Self.FLogAppenders and Self.FLoggerThread.FAppenders are the same list:

  i := Self.FLoggerThread.FAppenders.IndexOf(aAppender);
  if i >= 0 then
    Self.FLoggerThread.FAppenders.Delete(i);

For deleting items from lists, I always count down from the last element to zero so the index is always valid. Saves you the boolean for the repeat termination.

Like (untested from the top of my head) this:

for i := FLoggerThread.Appenders.Count -1 downto 0 do 
  if Self.FLoggerThread.FAppendersDecorators[i].FLogAppender = aAppender then
    begin
      Self.FLoggerThread.FAppendersDecorators.Delete(i);
      break;
    end;

@luebbe Edit: I get it! Your version of the loop allways evaluates the Count property so in case of delete the decrease of count prevent the index to go out of range.

It's true that you can break the loop after the delete but I think for is intended to loop trought the entire range. repeat until on the other hand can stop at some point. This is my personal opinion!