> As a goroutine may terminate if panic occurs
If that happens, it will crash the entire program (go isn't python :-)
> I used recover() to log the error and the monitoring thread is re-created. But this doesn't help either. I noticed that the goroutine has been terminated but error hasn't been logged.
How are you sure then, that the goroutine has terminated, not that it's just stuck?
In any case, you shouldn't attempt to recover from a system-related panic (e.g. out of memory). The system will be in an invalid state; better let it crash and be restarted by some external supervisor. You can recover from an explicit panic() raised in your code, which is sometimes useful when unwinding the stack many levels, e.g. to terminate some deeply-nested recursive function, but that's something else.
> Each sensor's state is maintained in sync.Map. The map maintains a pointer to the state.
Whilst sync.Map protects against concurrent read/write access to the map itself, it doesn't protect against how you use that data. If you retrieve a pointer, and then read or write via that pointer from different goroutines, then you'll still need to protect those accesses with a mutex. It looks like you're doing this in the reading loop; we don't see the code which writes to the state.
However, the go maxim is "
share memory by communicating". Could you arrange it so that your data collection process sends data down a channel, instead of updating shared memory?