Arc distance.

Before it rolls off the edge, can use the arc distance formula
$$d = r \theta$$
Where d is the distance travelled, r the radius of the cylinder (half y (or x) dimension) and theta the total angle in radians.
An empty is added the scene at location of cylinder at frame 1.
Or as demonstrated in callback "hack" (one of my favourites)
import bpy
from math import degrees
def callback():
from bpy import context
mt = context.scene.objects.get("Empty")
ob = context.scene.objects.get("Cylinder")
t = context.scene.objects.get("Text")
if not (t and mt and ob):
return None
radius = ob.dimensions.y / 2
distance = (
mt.matrix_world.translation
- ob.matrix_world.translation
).length
t.data.body = f"{degrees(distance / radius) :4.0f}"
def register():
global view_handler
view_handler = bpy.types.SpaceView3D.draw_handler_add(callback, (), 'WINDOW', 'PRE_VIEW')
def unregister():
bpy.types.SpaceView3D.draw_handler_remove(view_handler, 'WINDOW') # remove handler added on end of register() function
if __name__ == "__main__":
register()
This can also be set up as a driver. Added a rotating arrow and drove its rotation using a global distance empty to cylinder, with half Y dimension for radius.
Cam tracked to barrel, text and triangle parented to camera. Triangle's rotation Z driven. Showing a big case of "Wagon-wheel effect"
Rendering.
To have the result rendered it could also be an idea to consider frame change handlers, as well as drivers as mentioned by Markus.-----
Related
Transformation constraint does not contain mapping?
Calculate sphere's rotation for rolling