Every package has a LogRecordImpl table in its own database. The SAP Mobile Server can send import messages with LogRecordImpl records as part of its response to replay requests (success or failure). LogRecord stores two types of logs.
The SAP Mobile Server can embed a "log" JSON array into the header of a server message; the array is written to the LogRecordImpl table by the client. The client application can also write its own records. Each entity has a method called newLogRecord, which allows the entity to write its own log record. The LogRecordImpl table has "component" and "entityKey" columns that associate the log record entry with a particular MBO and primary key value.
SUPObjectList *salesorders = [SMP101Sales_order findAll]; if([salesorders size] > 0) { SMP101Sales_order * so = [salesorders item:0]; SMP101LogRecordImpl *lr = [SMP101LogRecordImpl getInstance]; lr.message = :@"testing record"]; lr.level = [SUPLogLevel INFO]; [lr save]; // submitting log records [SMP101SMP101DB submitLogRecords]; // synchronize with server [SMP101SMP101DB synchronize:@"system"]; } }
You can use the getLogRecords method to return log records from the table.
SUPQuery *query = [SUPQuery getInstance]; SUPObjectList *loglist = [SMP101SMP101DB getLogRecords:query]; for(id o in loglist) { LogRecordImpl *log = (LogRecordImpl*)o; MBOLogError(@"Log Record %llu: Operation = %@, Timestamp = %@, MBO = %@, key= %@,message=%@",log.messageId,log.operation, [SUPDateTimeUtil toString:log.timestamp],log.component,log.entityKey,log.message); }
Each mobile business object has a getLogRecords instance method that returns a list of all the log records that have been recorded for a particular entity row in a mobile business object:
SUPObjectList *salesorders = [SMP101Sales_order findAll]; if([salesorders size] > 0) { SMP101Sales_order * so = [salesorders item:0]; SUPObjectList *loglist = [so getLogRecords]; for(id o in loglist) { LogRecordImpl *log = (LogRecordImpl*)o; MBOLogError(@"Log Record %llu: Operation = %@, Timestamp = %@, MBO = %@, key= %@,message=%@",log.messageId,log.operation, [SUPDateTimeUtil toString:log.timestamp],log.component,log.entityKey,log.message); } }
Mobile business objects that support dynamic queries can be queried using the synthetic attribute hasLogRecords. This attribute generates a subquery that returns true if an entity row has any log records in the database, otherwise it returns false. The following code example prints out a list of customers, including first name, last name, and whether the customer row has log records:
SUPQuery *query = [SUPQuery getInstance]; [query select:@"x.surrogateKey,x.fname,x.lname,x.hasLogRecords"]; [query from:@"Customer":@"x"]; SUPQueryResultSet *qrs = [SMP101SMP101DB executeQuery:query]; MBOLogError(@"%@",[qrs.columnNames toString]); for(SUPDataValueList *row in qrs.array) { MBOLogError(@"%@",[row toString]); }
If there are a large number of rows in the MBO table, but only a few have log records associated with them, you may want to keep an in-memory object to track which rows have log records. You can define a class property as follows:
NSMutableArray* customerKeysWithLogRecords;
After data is downloaded from the server, initialize the array:
customerKeysWithLogRecords = [[NSMutableArray alloc] initWithCapacity:20]; SUPObjectList *allLogRecords = [SMP101SMP101DB getLogRecords:nil]; for(id<SUPLogRecord> lr in allLogRecords) { if(([lr entityKey] != nil) && ([[lr component] compare:@"Customer"] == 0)) [customerKeysWithLogRecords addObject:[lr entityKey]]; }
You do not need database access to determine if a row in the Customer MBO has a log record. The following expression returns true if a row has a log record:
BOOL hasALogRecord = [customerKeysWithLogRecords containsObject: [customerRow keyToString]];
This sample code shows how to find the corresponding MBO with the LogRecord and to delete the log record when a record is processed.
- (void)processLogs { SUPQuery *query = [SUPQuery getInstance]; SUPObjectList *logRecords = [SMP101SMP101DB getLogRecords:query]; for(id<SUPLogRecord> log in logRecords) { // Log warning message NSLog(@"log %@: %@ code:%d msg:%@",[log component],[log entityKey],[log code],[log message]); if([[log component] isEqualToString:@"Customer"]) { NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; int64_t surrogateKey = [[formatter numberFromString:[log entityKey]] longLongValue]; [formatter release]; SMP101Customer *c = [SMP101Customer find:surrogateKey]; if(c.pending) [c cancelPending]; [log delete]; [log submitPending]; } } [SMP101SMP101DB beginSynchronize]; }
private void processLogs() { Query query = new Query(); GenericList<LogRecord> logRecords = SMP101DB.getLogRecords(query); for(LogRecord log : logRecords) { // log warning message Log.warning("log " + log.getComponent() + ":" + log.getEntityKey() + " code:" + log.getCode() + " msg:" + log.getMessage()); if (log.getComponent().equals("Customer")) { long surrogateKey = Long.parseLong(log.getEntityKey()); Customer c = Customer.find(surrogateKey); if (c.isPending()) { c.cancelPending(); } // delete the LogRecord after it is processed log.delete(); log.submitPending(); } }SAP Mobile Server is responsible for deleting client log records uploaded by the application. These application logs are used for audit and/or support services. Determine and set the retention policy from SAP Control Center after consulting with the application's developers. If there are multiple applications using the same package, retain them based on the maximum required time for each application. Client log records are removed that are outside the retention window, and deleted records removed from the client database the next time the application synchronizes. See Improve Synchronization Performance by Reducing the Log Record Size in Troubleshooting for details about reducing the Log Record size.