/// <summary>
 /// Asynchronously adds a log to storage
 /// </summary>
 /// <param name="channelName">The name of the channel associated with the log</param>
 /// <param name="log">The log to add</param>
 /// <exception cref="StorageException"/>
 public Task PutLog(string channelName, Log log)
 {
     return(AddTaskToQueue(() =>
     {
         var logJsonString = LogSerializer.Serialize(log);
         var maxSize = _storageAdapter.GetMaxStorageSize();
         var logSize = Encoding.UTF8.GetBytes(logJsonString).Length;
         if (maxSize < 0)
         {
             throw new StorageException("Failed to store a log to the database.");
         }
         if (maxSize <= logSize)
         {
             throw new StorageException($"Log is too large ({logSize} bytes) to store in database. " +
                                        $"Current maximum database size is {maxSize} bytes.");
         }
         while (true)
         {
             try
             {
                 _storageAdapter.Insert(TableName,
                                        new[] { ColumnChannelName, ColumnLogName },
                                        new List <object[]> {
                     new object[] { channelName, logJsonString }
                 });
                 return;
             }
             catch (StorageFullException)
             {
                 var oldestLog = _storageAdapter.Select(TableName, ColumnChannelName, channelName, string.Empty, null, 1, new string[] { ColumnIdName });
                 if (oldestLog != null && oldestLog.Count > 0 && oldestLog[0].Length > 0)
                 {
                     _storageAdapter.Delete(TableName, ColumnIdName, oldestLog[0][0]);
                 }
                 else
                 {
                     throw new StorageException("Failed to add a new log. Storage is full and old logs cannot be purged.");
                 }
             }
         }
     }));
 }
예제 #2
0
        /// <summary>
        /// Asynchronously retrieves logs from storage and flags them to avoid duplicate retrievals on subsequent calls
        /// </summary>
        /// <param name="channelName">Name of the channel to retrieve logs from</param>
        /// <param name="limit">The maximum number of logs to retrieve</param>
        /// <param name="logs">A list to which the retrieved logs will be added</param>
        /// <returns>A batch ID for the set of returned logs; null if no logs are found</returns>
        /// <exception cref="StorageException"/>
        public Task <string> GetLogsAsync(string channelName, int limit, List <Log> logs)
        {
            return(AddTaskToQueue(() =>
            {
                logs?.Clear();
                var retrievedLogs = new List <Log>();
                AppCenterLog.Debug(AppCenterLog.LogTag,
                                   $"Trying to get up to {limit} logs from storage for {channelName}");
                var idPairs = new List <Tuple <Guid?, long> >();
                var failedToDeserializeALog = false;
                var objectEntries = _storageAdapter.Select(TableName, ColumnChannelName, channelName, ColumnIdName, _pendingDbIdentifiers.Cast <object>().ToArray(), limit);
                var retrievedEntries = objectEntries.Select(entries =>
                                                            new LogEntry()
                {
                    Id = (long)entries[0],
                    Channel = (string)entries[1],
                    Log = (string)entries[2]
                }
                                                            ).ToList();
                foreach (var entry in retrievedEntries)
                {
                    try
                    {
                        var log = LogSerializer.DeserializeLog(entry.Log);
                        retrievedLogs.Add(log);
                        idPairs.Add(Tuple.Create(log.Sid, Convert.ToInt64(entry.Id)));
                    }
                    catch (JsonException e)
                    {
                        AppCenterLog.Error(AppCenterLog.LogTag, "Cannot deserialize a log in storage", e);
                        failedToDeserializeALog = true;
                        _storageAdapter.Delete(TableName, ColumnIdName, entry.Id);
                    }
                }
                if (failedToDeserializeALog)
                {
                    AppCenterLog.Warn(AppCenterLog.LogTag, "Deleted logs that could not be deserialized");
                }
                if (idPairs.Count == 0)
                {
                    AppCenterLog.Debug(AppCenterLog.LogTag,
                                       $"No available logs in storage for channel '{channelName}'");
                    return null;
                }

                // Process the results
                var batchId = Guid.NewGuid().ToString();
                ProcessLogIds(channelName, batchId, idPairs);
                logs?.AddRange(retrievedLogs);
                return batchId;
            }));
        }