brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 1 | #include <string.h> |
| 2 | |
| 3 | #include "WPILib/Timer.h" |
| 4 | #include "WPILib/Task.h" |
| 5 | |
| 6 | #include "aos/aos_core.h" |
| 7 | #include "aos/common/network/SendSocket.h" |
| 8 | #include "aos/common/Configuration.h" |
| 9 | #include "aos/common/die.h" |
| 10 | |
| 11 | #undef ERROR |
| 12 | #define DECL_LEVEL(name, value) const log_level name = value; |
| 13 | DECL_LEVELS |
| 14 | #undef DECL_LEVEL |
| 15 | |
| 16 | //#define fprintf(...) |
| 17 | |
| 18 | namespace aos { |
| 19 | namespace logging { |
| 20 | namespace { |
| 21 | |
| 22 | MSG_Q_ID queue; |
| 23 | uint8_t sequence = 0; |
| 24 | |
| 25 | // This gets run in a low-priority task to take the logs from the queue and send |
| 26 | // them to the atom over a TCP socket. |
| 27 | void DoTask() { |
| 28 | SendSocket sock; |
| 29 | log_crio_message msg; |
| 30 | while (true) { |
| 31 | const int ret = msgQReceive(queue, reinterpret_cast<char *>(&msg), |
| 32 | sizeof(msg), WAIT_FOREVER); |
| 33 | if (ret == ERROR) { |
| 34 | fprintf(stderr, "logging: warning: receiving a message failed" |
| 35 | " with %d (%s)", errno, strerror(errno)); |
| 36 | continue; |
| 37 | } |
| 38 | if (ret != sizeof(msg)) { |
| 39 | fprintf(stderr, "logging: warning: received a message of size %d " |
| 40 | "instead of %zd\n", ret, sizeof(msg)); |
| 41 | continue; |
| 42 | } |
| 43 | |
| 44 | if (sock.LastStatus() != 0) { |
| 45 | if (sock.Connect(NetworkPort::kLogs, |
| 46 | configuration::GetIPAddress( |
| 47 | configuration::NetworkDevice::kAtom), |
| 48 | SOCK_STREAM) != 0) { |
| 49 | fprintf(stderr, "logging: warning: connecting failed" |
| 50 | " because of %d: %s\n", errno, strerror(errno)); |
| 51 | } |
| 52 | } |
| 53 | sock.Send(&msg, sizeof(msg)); |
| 54 | if (sock.LastStatus() != 0) { |
| 55 | fprintf(stderr, "logging: warning: sending '%s' failed" |
| 56 | " because of %d: %s\n", msg.message, errno, strerror(errno)); |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | } // namespace |
| 62 | |
| 63 | void Start() { |
| 64 | queue = msgQCreate(100, // max messages |
| 65 | sizeof(log_crio_message), |
| 66 | MSG_Q_PRIORITY); |
| 67 | Task *task = new Task("LogSender", |
| 68 | (FUNCPTR)(DoTask), |
| 69 | 150); // priority |
| 70 | task->Start(); |
| 71 | } |
| 72 | |
| 73 | int Do(log_level level, const char *format, ...) { |
| 74 | log_crio_message msg; |
| 75 | msg.time = Timer::GetFPGATimestamp(); |
| 76 | msg.level = level; |
| 77 | msg.sequence = __sync_fetch_and_add(&sequence, 1); |
| 78 | |
| 79 | const char *continued = "..."; |
| 80 | const size_t size = sizeof(msg.message) - strlen(continued); |
| 81 | va_list ap; |
| 82 | va_start(ap, format); |
| 83 | const int ret = vsnprintf(msg.message, size, format, ap); |
| 84 | va_end(ap); |
| 85 | |
| 86 | if (ret < 0) { |
| 87 | fprintf(stderr, "logging: error: vsnprintf failed with %d (%s)\n", |
| 88 | errno, strerror(errno)); |
| 89 | return -1; |
| 90 | } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) { |
| 91 | // overwrite the NULL at the end of the existing one and |
| 92 | // copy in the one on the end of continued |
| 93 | memcpy(&msg.message[size - 1], continued, strlen(continued) + 1); |
| 94 | } |
| 95 | if (msgQSend(queue, reinterpret_cast<char *>(&msg), sizeof(msg), |
| 96 | NO_WAIT, MSG_PRI_NORMAL) == ERROR) { |
| 97 | fprintf(stderr, "logging: warning: sending message '%s'" |
| 98 | " failed with %d (%s)", msg.message, errno, strerror(errno)); |
| 99 | return -1; |
| 100 | } |
| 101 | |
| 102 | if (level == FATAL) { |
| 103 | aos::Die("%s", msg.message); |
| 104 | } |
| 105 | |
| 106 | return 0; |
| 107 | } |
| 108 | |
| 109 | } // namespace logging |
| 110 | } // namespace aos |